@savvy-web/silk-effects 1.5.2 → 1.6.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.
@@ -1,8 +1,10 @@
1
1
  import { ContentStructureRule } from "../remark/rules/content-structure.js";
2
+ import { DependencyTableFormatRule } from "../remark/rules/dependency-table-format.js";
2
3
  import { HeadingHierarchyRule } from "../remark/rules/heading-hierarchy.js";
3
4
  import { RequiredSectionsRule } from "../remark/rules/required-sections.js";
4
5
  import { UncategorizedContentRule } from "../remark/rules/uncategorized-content.js";
5
6
  import { stripFrontmatter } from "../utils/strip-frontmatter.js";
7
+ import remarkGfm from "remark-gfm";
6
8
  import remarkParse from "remark-parse";
7
9
  import remarkStringify from "remark-stringify";
8
10
  import { unified } from "unified";
@@ -13,7 +15,7 @@ import { join } from "node:path";
13
15
  /**
14
16
  * Class-based API wrapper for changeset linting.
15
17
  *
16
- * Provides a static class interface that runs all remark-lint rules
18
+ * Provides a static class interface that runs all five remark-lint rules
17
19
  * against changeset markdown files and returns structured diagnostics.
18
20
  *
19
21
  * @internal
@@ -21,9 +23,9 @@ import { join } from "node:path";
21
23
  /**
22
24
  * Static class for linting changeset markdown files.
23
25
  *
24
- * Runs the four remark-lint rules (heading-hierarchy, required-sections,
25
- * content-structure, uncategorized-content) against changeset markdown
26
- * and returns structured {@link LintMessage} diagnostics.
26
+ * Runs the five remark-lint rules (heading-hierarchy, required-sections,
27
+ * content-structure, dependency-table-format, uncategorized-content) against
28
+ * changeset markdown and returns structured {@link LintMessage} diagnostics.
27
29
  *
28
30
  * @remarks
29
31
  * This class implements the pre-validation layer of the three-layer
@@ -109,7 +111,7 @@ var ChangesetLinter = class ChangesetLinter {
109
111
  *
110
112
  * @remarks
111
113
  * Reads the file synchronously, strips YAML frontmatter, and runs all
112
- * four lint rules. The file path is preserved in each returned
114
+ * five lint rules. The file path is preserved in each returned
113
115
  * {@link LintMessage} for error reporting.
114
116
  *
115
117
  * @param filePath - Absolute or relative path to the changeset `.md` file
@@ -123,7 +125,7 @@ var ChangesetLinter = class ChangesetLinter {
123
125
  * Validate a markdown string directly.
124
126
  *
125
127
  * @remarks
126
- * Strips YAML frontmatter (if present) and runs all four lint rules
128
+ * Strips YAML frontmatter (if present) and runs all five lint rules
127
129
  * against the remaining content. This method is useful for validating
128
130
  * changeset content that is already in memory, such as in test suites
129
131
  * or editor integrations.
@@ -135,7 +137,7 @@ var ChangesetLinter = class ChangesetLinter {
135
137
  */
136
138
  static validateContent(content, filePath = "<input>") {
137
139
  const body = stripFrontmatter(content);
138
- return unified().use(remarkParse).use(remarkStringify).use(HeadingHierarchyRule).use(RequiredSectionsRule).use(ContentStructureRule).use(UncategorizedContentRule).processSync(body).messages.map((msg) => ({
140
+ return unified().use(remarkParse).use(remarkGfm).use(remarkStringify).use(HeadingHierarchyRule).use(RequiredSectionsRule).use(ContentStructureRule).use(DependencyTableFormatRule).use(UncategorizedContentRule).processSync(body).messages.map((msg) => ({
139
141
  file: filePath,
140
142
  /* v8 ignore next 3 -- ruleId/line/column fallbacks; remark-lint always provides these */
141
143
  rule: msg.ruleId ?? msg.source ?? "unknown",
@@ -7,13 +7,14 @@ import { ChangesetOptionsSchema, RepoSchema } from "./schemas/options.js";
7
7
  import { GitHubLive, GitHubService, GitHubServiceBase, makeGitHubTest } from "./services/github.js";
8
8
  import { MarkdownLive, MarkdownService, MarkdownServiceBase } from "./services/markdown.js";
9
9
  import { NonEmptyString, PositiveInteger } from "./schemas/primitives.js";
10
- import { DependencyActionSchema, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, VersionOrEmptySchema } from "./schemas/dependency-table.js";
10
+ import { DependencyActionSchema, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, VERSION_RE, VersionOrEmptySchema } from "./schemas/dependency-table.js";
11
11
  import { serializeDependencyTableToMarkdown } from "./utils/dependency-table.js";
12
12
  import { GitHubInfoSchema, IssueNumberSchema, UrlOrMarkdownLinkSchema, UsernameSchema } from "./schemas/github.js";
13
13
  import changelogFunctions from "./changelog/index.js";
14
14
  import { Changelog } from "./api/changelog.js";
15
15
  import { DependencyTable } from "./api/dependency-table.js";
16
16
  import { ContentStructureRule } from "./remark/rules/content-structure.js";
17
+ import { DependencyTableFormatRule } from "./remark/rules/dependency-table-format.js";
17
18
  import { HeadingHierarchyRule } from "./remark/rules/heading-hierarchy.js";
18
19
  import { RequiredSectionsRule } from "./remark/rules/required-sections.js";
19
20
  import { UncategorizedContentRule } from "./remark/rules/uncategorized-content.js";
@@ -28,24 +29,24 @@ import { ChangelogTransformer } from "./api/transformer.js";
28
29
  import { ClassificationReasonSchema, ClassificationSchema, ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, InspectedConfigSchema, ResolvedPackageScopeSchema, ResolvedVersionFileSchema, makeConfigInspectorTest } from "./services/config-inspector.js";
29
30
  import { BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchFileEntrySchema, FileStatusSchema, makeBranchAnalyzerTest } from "./services/branch-analyzer.js";
30
31
  import { ChangelogService, ChangelogServiceBase } from "./services/changelog.js";
32
+ import { computeWorkspaceDependencyDiffs } from "./utils/dep-diff.js";
33
+ import { listPublishablePackageNames } from "./utils/publishability.js";
34
+ import { gitMergeBase, snapshotFromWorktree } from "./utils/worktree-snapshot.js";
35
+ import { WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive } from "./services/workspace-snapshot.js";
36
+ import { DepsRegen, DepsRegenBase, DepsRegenLive, isPureDependencyChangeset, resolveDiffRows } from "./services/deps-regen.js";
31
37
  import { VersionFiles } from "./utils/version-files.js";
32
38
  import { ReleasePlanner, ReleasePlannerBase, ReleasePlannerLive, makeReleasePlannerTest } from "./services/release-planner.js";
33
- import { WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive } from "./services/workspace-snapshot.js";
34
39
  import { SectionCategorySchema } from "./categories/types.js";
35
40
  import { CommitHashSchema, VersionTypeSchema } from "./schemas/git.js";
36
41
  import { ChangesetSchema, ChangesetSummarySchema, DependencyTypeSchema, DependencyUpdateSchema } from "./schemas/changeset.js";
37
42
  import { AppliedReleaseEntrySchema, AppliedReleaseSchema, BumpTypeSchema, ChangesetPreviewSchema, PendingChangesetSchema, PreviewReleaseSchema, VersionFileUpdateRecordSchema } from "./schemas/release-plan.js";
38
- import { computeWorkspaceDependencyDiffs } from "./utils/dep-diff.js";
39
- import { listPublishablePackageNames } from "./utils/publishability.js";
40
- import { gitMergeBase, snapshotFromWorktree } from "./utils/worktree-snapshot.js";
41
43
  import { ContentStructureRule as ContentStructureRule$1 } from "./markdownlint/rules/content-structure.js";
42
- import { DependencyTableFormatRule } from "./markdownlint/rules/dependency-table-format.js";
44
+ import { DependencyTableFormatRule as DependencyTableFormatRule$1 } from "./markdownlint/rules/dependency-table-format.js";
43
45
  import { HeadingHierarchyRule as HeadingHierarchyRule$1 } from "./markdownlint/rules/heading-hierarchy.js";
44
46
  import { RequiredSectionsRule as RequiredSectionsRule$1 } from "./markdownlint/rules/required-sections.js";
45
47
  import { UncategorizedContentRule as UncategorizedContentRule$1 } from "./markdownlint/rules/uncategorized-content.js";
46
48
  import SilkChangesetsRules from "./markdownlint/index.js";
47
49
  import { AggregateDependencyTablesPlugin } from "./remark/plugins/aggregate-dependency-tables.js";
48
- import { DependencyTableFormatRule as DependencyTableFormatRule$1 } from "./remark/rules/dependency-table-format.js";
49
50
  import { SilkChangesetPreset, SilkChangesetTransformPreset } from "./remark/presets.js";
50
51
 
51
52
  //#region src/changesets/index.ts
@@ -84,12 +85,15 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
84
85
  DeduplicateItemsPlugin: () => DeduplicateItemsPlugin,
85
86
  DependencyActionSchema: () => DependencyActionSchema,
86
87
  DependencyTable: () => DependencyTable,
87
- DependencyTableFormatRule: () => DependencyTableFormatRule$1,
88
+ DependencyTableFormatRule: () => DependencyTableFormatRule,
88
89
  DependencyTableRowSchema: () => DependencyTableRowSchema,
89
90
  DependencyTableSchema: () => DependencyTableSchema,
90
91
  DependencyTableTypeSchema: () => DependencyTableTypeSchema,
91
92
  DependencyTypeSchema: () => DependencyTypeSchema,
92
93
  DependencyUpdateSchema: () => DependencyUpdateSchema,
94
+ DepsRegen: () => DepsRegen,
95
+ DepsRegenBase: () => DepsRegenBase,
96
+ DepsRegenLive: () => DepsRegenLive,
93
97
  FileStatusSchema: () => FileStatusSchema,
94
98
  GitError: () => GitError,
95
99
  GitErrorBase: () => GitErrorBase,
@@ -113,7 +117,7 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
113
117
  MarkdownService: () => MarkdownService,
114
118
  MarkdownServiceBase: () => MarkdownServiceBase,
115
119
  MarkdownlintContentStructureRule: () => ContentStructureRule$1,
116
- MarkdownlintDependencyTableFormatRule: () => DependencyTableFormatRule,
120
+ MarkdownlintDependencyTableFormatRule: () => DependencyTableFormatRule$1,
117
121
  MarkdownlintHeadingHierarchyRule: () => HeadingHierarchyRule$1,
118
122
  MarkdownlintRequiredSectionsRule: () => RequiredSectionsRule$1,
119
123
  MarkdownlintUncategorizedContentRule: () => UncategorizedContentRule$1,
@@ -142,6 +146,7 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
142
146
  UncategorizedContentRule: () => UncategorizedContentRule,
143
147
  UrlOrMarkdownLinkSchema: () => UrlOrMarkdownLinkSchema,
144
148
  UsernameSchema: () => UsernameSchema,
149
+ VERSION_RE: () => VERSION_RE,
145
150
  VersionFileConfigSchema: () => VersionFileConfigSchema,
146
151
  VersionFileError: () => VersionFileError,
147
152
  VersionFileErrorBase: () => VersionFileErrorBase,
@@ -156,14 +161,16 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
156
161
  changelogFunctions: () => changelogFunctions,
157
162
  computeWorkspaceDependencyDiffs: () => computeWorkspaceDependencyDiffs,
158
163
  gitMergeBase: () => gitMergeBase,
164
+ isPureDependencyChangeset: () => isPureDependencyChangeset,
159
165
  listPublishablePackageNames: () => listPublishablePackageNames,
160
166
  makeBranchAnalyzerTest: () => makeBranchAnalyzerTest,
161
167
  makeConfigInspectorTest: () => makeConfigInspectorTest,
162
168
  makeGitHubTest: () => makeGitHubTest,
163
169
  makeReleasePlannerTest: () => makeReleasePlannerTest,
170
+ resolveDiffRows: () => resolveDiffRows,
164
171
  serializeDependencyTableToMarkdown: () => serializeDependencyTableToMarkdown,
165
172
  snapshotFromWorktree: () => snapshotFromWorktree
166
173
  });
167
174
 
168
175
  //#endregion
169
- export { AggregateDependencyTablesPlugin, AppliedReleaseEntrySchema, AppliedReleaseSchema, BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchFileEntrySchema, BumpTypeSchema, Categories, Changelog, ChangelogService, ChangelogServiceBase, ChangelogTransformer, ChangesetLinter, ChangesetOptionsSchema, ChangesetPreviewSchema, ChangesetSchema, ChangesetSummarySchema, ChangesetValidationError, ChangesetValidationErrorBase, ClassificationReasonSchema, ClassificationSchema, CommitHashSchema, ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, ConfigurationError, ConfigurationErrorBase, ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, DependencyActionSchema, DependencyTable, DependencyTableFormatRule$1 as DependencyTableFormatRule, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, DependencyTypeSchema, DependencyUpdateSchema, FileStatusSchema, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, GitHubInfoSchema, GitHubLive, GitHubService, GitHubServiceBase, GlobSchema, HeadingHierarchyRule, InspectedConfigSchema, IssueLinkRefsPlugin, IssueNumberSchema, JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, MarkdownLive, MarkdownParseError, MarkdownParseErrorBase, MarkdownService, MarkdownServiceBase, ContentStructureRule$1 as MarkdownlintContentStructureRule, DependencyTableFormatRule as MarkdownlintDependencyTableFormatRule, HeadingHierarchyRule$1 as MarkdownlintHeadingHierarchyRule, RequiredSectionsRule$1 as MarkdownlintRequiredSectionsRule, UncategorizedContentRule$1 as MarkdownlintUncategorizedContentRule, MergeSectionsPlugin, NonEmptyString, NormalizeFormatPlugin, PackageScopeSchema, PackagesRecordSchema, PendingChangesetSchema, PositiveInteger, PreviewReleaseSchema, ReleasePlanError, ReleasePlanErrorBase, ReleasePlanner, ReleasePlannerBase, ReleasePlannerLive, ReorderSectionsPlugin, RepoSchema, RequiredSectionsRule, ResolvedPackageScopeSchema, ResolvedVersionFileSchema, SectionCategorySchema, SilkChangesetPreset, SilkChangesetTransformPreset, SilkChangesetsRules, UncategorizedContentRule, UrlOrMarkdownLinkSchema, UsernameSchema, VersionFileConfigSchema, VersionFileError, VersionFileErrorBase, VersionFileUpdateRecordSchema, VersionFiles, VersionFilesSchema, VersionOrEmptySchema, VersionTypeSchema, WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive, changelogFunctions, changesets_exports, computeWorkspaceDependencyDiffs, gitMergeBase, listPublishablePackageNames, makeBranchAnalyzerTest, makeConfigInspectorTest, makeGitHubTest, makeReleasePlannerTest, serializeDependencyTableToMarkdown, snapshotFromWorktree };
176
+ export { AggregateDependencyTablesPlugin, AppliedReleaseEntrySchema, AppliedReleaseSchema, BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchFileEntrySchema, BumpTypeSchema, Categories, Changelog, ChangelogService, ChangelogServiceBase, ChangelogTransformer, ChangesetLinter, ChangesetOptionsSchema, ChangesetPreviewSchema, ChangesetSchema, ChangesetSummarySchema, ChangesetValidationError, ChangesetValidationErrorBase, ClassificationReasonSchema, ClassificationSchema, CommitHashSchema, ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, ConfigurationError, ConfigurationErrorBase, ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, DependencyActionSchema, DependencyTable, DependencyTableFormatRule, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, DependencyTypeSchema, DependencyUpdateSchema, DepsRegen, DepsRegenBase, DepsRegenLive, FileStatusSchema, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, GitHubInfoSchema, GitHubLive, GitHubService, GitHubServiceBase, GlobSchema, HeadingHierarchyRule, InspectedConfigSchema, IssueLinkRefsPlugin, IssueNumberSchema, JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, MarkdownLive, MarkdownParseError, MarkdownParseErrorBase, MarkdownService, MarkdownServiceBase, ContentStructureRule$1 as MarkdownlintContentStructureRule, DependencyTableFormatRule$1 as MarkdownlintDependencyTableFormatRule, HeadingHierarchyRule$1 as MarkdownlintHeadingHierarchyRule, RequiredSectionsRule$1 as MarkdownlintRequiredSectionsRule, UncategorizedContentRule$1 as MarkdownlintUncategorizedContentRule, MergeSectionsPlugin, NonEmptyString, NormalizeFormatPlugin, PackageScopeSchema, PackagesRecordSchema, PendingChangesetSchema, PositiveInteger, PreviewReleaseSchema, ReleasePlanError, ReleasePlanErrorBase, ReleasePlanner, ReleasePlannerBase, ReleasePlannerLive, ReorderSectionsPlugin, RepoSchema, RequiredSectionsRule, ResolvedPackageScopeSchema, ResolvedVersionFileSchema, SectionCategorySchema, SilkChangesetPreset, SilkChangesetTransformPreset, SilkChangesetsRules, UncategorizedContentRule, UrlOrMarkdownLinkSchema, UsernameSchema, VERSION_RE, VersionFileConfigSchema, VersionFileError, VersionFileErrorBase, VersionFileUpdateRecordSchema, VersionFiles, VersionFilesSchema, VersionOrEmptySchema, VersionTypeSchema, WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive, changelogFunctions, changesets_exports, computeWorkspaceDependencyDiffs, gitMergeBase, isPureDependencyChangeset, listPublishablePackageNames, makeBranchAnalyzerTest, makeConfigInspectorTest, makeGitHubTest, makeReleasePlannerTest, resolveDiffRows, serializeDependencyTableToMarkdown, snapshotFromWorktree };
@@ -1,3 +1,4 @@
1
+ import { VERSION_RE } from "../../schemas/dependency-table.js";
1
2
  import { RULE_DOCS } from "../../constants.js";
2
3
  import { getHeadingLevel, getHeadingText } from "./utils.js";
3
4
 
@@ -23,7 +24,6 @@ const EXPECTED_HEADERS = [
23
24
  "from",
24
25
  "to"
25
26
  ];
26
- const VERSION_RE = /^(\u2014|[~^]?\d+\.\d+\.\d+(?:[-+.][\w.+-]*)?)$/;
27
27
  /**
28
28
  * Extract text from a `tableHeader` or `tableData` cell token.
29
29
  *
@@ -1,4 +1,5 @@
1
1
  import { ContentStructureRule } from "./rules/content-structure.js";
2
+ import { DependencyTableFormatRule } from "./rules/dependency-table-format.js";
2
3
  import { HeadingHierarchyRule } from "./rules/heading-hierarchy.js";
3
4
  import { RequiredSectionsRule } from "./rules/required-sections.js";
4
5
  import { UncategorizedContentRule } from "./rules/uncategorized-content.js";
@@ -9,7 +10,6 @@ import { MergeSectionsPlugin } from "./plugins/merge-sections.js";
9
10
  import { NormalizeFormatPlugin } from "./plugins/normalize-format.js";
10
11
  import { ReorderSectionsPlugin } from "./plugins/reorder-sections.js";
11
12
  import { AggregateDependencyTablesPlugin } from "./plugins/aggregate-dependency-tables.js";
12
- import { DependencyTableFormatRule } from "./rules/dependency-table-format.js";
13
13
 
14
14
  //#region src/changesets/remark/presets.ts
15
15
  /**
@@ -68,6 +68,16 @@ const DependencyActionSchema = Schema.Literal("added", "updated", "removed");
68
68
  */
69
69
  const DependencyTableTypeSchema = Schema.Literal("dependency", "devDependency", "peerDependency", "optionalDependency", "workspace", "config");
70
70
  /**
71
+ * The canonical accepted-value pattern for a dependency-table From/To cell:
72
+ * the em-dash sentinel (U+2014), a bare/`~`/`^` semver, or — as a last-resort
73
+ * fallback when a `catalog:`/`workspace:` specifier could not be resolved to a
74
+ * concrete version — a pnpm protocol string. Non-overlapping alternatives keep
75
+ * this free of polynomial backtracking (CodeQL).
76
+ *
77
+ * @public
78
+ */
79
+ const VERSION_RE = /^(\u2014|[~^]?\d+\.\d+\.\d+(?:[-+.][\w.+-]+)?|(?:catalog|workspace|npm|jsr|file|link|portal):[^\s|]+)$/;
80
+ /**
71
81
  * Version string or em dash (U+2014) sentinel for added/removed entries.
72
82
  *
73
83
  * @remarks
@@ -93,7 +103,7 @@ const DependencyTableTypeSchema = Schema.Literal("dependency", "devDependency",
93
103
  *
94
104
  * @public
95
105
  */
96
- const VersionOrEmptySchema = Schema.String.pipe(Schema.pattern(/^(\u2014|[~^]?\d+\.\d+\.\d+(?:[-+.][\w.+-]*)?)$/));
106
+ const VersionOrEmptySchema = Schema.String.pipe(Schema.pattern(VERSION_RE));
97
107
  /**
98
108
  * Schema for a single dependency table row.
99
109
  *
@@ -185,4 +195,4 @@ const DependencyTableRowSchema = Schema.Struct({
185
195
  const DependencyTableSchema = Schema.Array(DependencyTableRowSchema).pipe(Schema.minItems(1));
186
196
 
187
197
  //#endregion
188
- export { DependencyActionSchema, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, VersionOrEmptySchema };
198
+ export { DependencyActionSchema, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, VERSION_RE, VersionOrEmptySchema };
@@ -0,0 +1,328 @@
1
+ import { serializeDependencyTableToMarkdown, sortDependencyRows } from "../utils/dependency-table.js";
2
+ import { ConfigInspector } from "./config-inspector.js";
3
+ import { computeWorkspaceDependencyDiffs } from "../utils/dep-diff.js";
4
+ import { listPublishablePackageNames } from "../utils/publishability.js";
5
+ import { gitMergeBase, snapshotFromWorktree } from "../utils/worktree-snapshot.js";
6
+ import { WorkspaceSnapshotReader } from "./workspace-snapshot.js";
7
+ import { Context, Effect, Layer, Option } from "effect";
8
+ import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
9
+ import { join, resolve } from "node:path";
10
+ import { CatalogResolver, PublishabilityDetector, WorkspaceDiscovery } from "workspaces-effect";
11
+
12
+ //#region src/changesets/services/deps-regen.ts
13
+ /**
14
+ * `Changesets.DepsRegen` service — lift the `deps regen` / `deps detect`
15
+ * orchestration out of the CLI into a `Context.Tag` service with a
16
+ * `plan()` / `execute()` split.
17
+ *
18
+ * @remarks
19
+ * `plan()` computes the cumulative dependency diff (merge-base → working
20
+ * tree by default, or between two explicit refs), resolves `catalog:` /
21
+ * `workspace:` specifiers to concrete versions, drops `devDependency`
22
+ * rows (unless `includeDevDeps`), and returns a complete {@link RegenPlan}
23
+ * (target filenames + stale-changeset deletes) WITHOUT touching the
24
+ * filesystem. `execute()` applies a plan — writing the fresh changesets
25
+ * first, then deleting the stale pure-dependency ones (so an interrupted
26
+ * run loses nothing and is safely re-runnable).
27
+ *
28
+ * This is the single source of truth for regen/detect: the CLI commands
29
+ * and MCP tools are thin adapters over this service.
30
+ *
31
+ * @see {@link DepsRegen} for the service tag
32
+ * @see {@link DepsRegenLive} for the production layer
33
+ *
34
+ */
35
+ /** The em-dash sentinel (U+2014) used for added ("from") / removed ("to") cells. */
36
+ const EM_DASH = "—";
37
+ /** Whether a From/To cell holds a pnpm protocol specifier that could resolve to a version. */
38
+ const isProtocol = (v) => /^(?:catalog|workspace|npm|jsr|file|link|portal):/.test(v);
39
+ /**
40
+ * Resolve protocol From/To cells to concrete versions (raw-string fallback
41
+ * when unresolved or on resolver error), leave em-dash sentinels untouched,
42
+ * then optionally drop `devDependency` rows, and re-sort.
43
+ *
44
+ * @param diff - One workspace package's dependency-table rows.
45
+ * @param keepDevDeps - When `true`, retain `devDependency` rows; otherwise
46
+ * drop them unconditionally (the regen default).
47
+ * @returns An Effect yielding the transformed {@link WorkspaceDependencyDiff}.
48
+ *
49
+ * @public
50
+ */
51
+ const resolveDiffRows = (diff, keepDevDeps = false) => Effect.gen(function* () {
52
+ const resolver = yield* CatalogResolver;
53
+ const resolveCell = (dep, value) => isProtocol(value) ? resolver.resolveSpecifier(dep, value).pipe(Effect.map((opt) => Option.getOrElse(opt, () => value)), Effect.catchAll((error) => Effect.logWarning(`DepsRegen: catalog resolution failed for "${dep}" (${value}); keeping raw specifier: ${String(error)}`).pipe(Effect.as(value)))) : Effect.succeed(value);
54
+ const rows = [];
55
+ for (const row of diff.rows) {
56
+ if (!keepDevDeps && row.type === "devDependency") continue;
57
+ const from = row.from === EM_DASH ? EM_DASH : yield* resolveCell(row.dependency, row.from);
58
+ const to = row.to === EM_DASH ? EM_DASH : yield* resolveCell(row.dependency, row.to);
59
+ rows.push({
60
+ ...row,
61
+ from,
62
+ to
63
+ });
64
+ }
65
+ return {
66
+ ...diff,
67
+ rows: sortDependencyRows(rows)
68
+ };
69
+ });
70
+ const ADJECTIVES = [
71
+ "brave",
72
+ "clever",
73
+ "swift",
74
+ "silver",
75
+ "lucky",
76
+ "happy",
77
+ "calm",
78
+ "bright",
79
+ "quiet",
80
+ "wild"
81
+ ];
82
+ const NOUNS = [
83
+ "dogs",
84
+ "cats",
85
+ "wolves",
86
+ "foxes",
87
+ "cups",
88
+ "ships",
89
+ "trees",
90
+ "owls",
91
+ "cranes",
92
+ "hills"
93
+ ];
94
+ const VERBS = [
95
+ "laugh",
96
+ "dream",
97
+ "fly",
98
+ "sing",
99
+ "dance",
100
+ "wander",
101
+ "soar",
102
+ "rest",
103
+ "leap",
104
+ "ponder"
105
+ ];
106
+ function pickRandomTriplet() {
107
+ return `${ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)]}-${NOUNS[Math.floor(Math.random() * NOUNS.length)]}-${VERBS[Math.floor(Math.random() * VERBS.length)]}`;
108
+ }
109
+ /**
110
+ * Pick a `<adjective>-<noun>-<verb>` filename slug that does not collide
111
+ * with an existing `.changeset/*.md` OR with a slug already claimed
112
+ * earlier in the same {@link RegenPlan.toWrite} computation. `plan()`
113
+ * never writes to disk, so `existsSync` alone cannot see slugs chosen
114
+ * moments earlier in the same call — the `chosen` set closes that gap.
115
+ * The triplet space is 1,000 combinations, so a busy repo can plausibly
116
+ * exhaust it across runs; fall back to a timestamp suffix after 20
117
+ * unlucky picks.
118
+ *
119
+ * @param changesetDir - Directory checked via `existsSync` for on-disk collisions.
120
+ * @param chosen - Basenames (without extension) already picked within this plan;
121
+ * the picked candidate is added to this set before returning.
122
+ * @internal
123
+ */
124
+ function randomFilename(changesetDir, chosen) {
125
+ for (let i = 0; i < 20; i++) {
126
+ const candidate = pickRandomTriplet();
127
+ if (!chosen.has(candidate) && !existsSync(join(changesetDir, `${candidate}.md`))) {
128
+ chosen.add(candidate);
129
+ return candidate;
130
+ }
131
+ }
132
+ let attempt = 0;
133
+ let fallback = `${pickRandomTriplet()}-${Date.now()}`;
134
+ while (chosen.has(fallback) || existsSync(join(changesetDir, `${fallback}.md`))) fallback = `${pickRandomTriplet()}-${Date.now()}-${++attempt}`;
135
+ chosen.add(fallback);
136
+ return fallback;
137
+ }
138
+ /**
139
+ * Strict detection of "pure dependency changesets" per the documented
140
+ * rules: single-package frontmatter, single `## Dependencies` heading,
141
+ * no other body content beyond that section.
142
+ *
143
+ * @param content - Raw `.changeset/*.md` file contents.
144
+ * @returns `{ isPure, package }` — `isPure` is `true` only for a
145
+ * single-package, Dependencies-only changeset; `package` is the sole
146
+ * frontmatter package name (or `null` when not pure).
147
+ *
148
+ * @public
149
+ */
150
+ function isPureDependencyChangeset(content) {
151
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
152
+ if (!fmMatch) return {
153
+ isPure: false,
154
+ package: null
155
+ };
156
+ const frontmatter = fmMatch[1];
157
+ const body = (fmMatch[2] ?? "").trim();
158
+ const fmLines = frontmatter.split(/\r?\n/).filter((l) => l.trim().length > 0 && !/^\s*#/.test(l));
159
+ if (fmLines.length !== 1) return {
160
+ isPure: false,
161
+ package: null
162
+ };
163
+ const pkgMatch = fmLines[0].match(/^\s*["']?([^"':\s]+)["']?\s*:\s*([a-z]+)\s*$/);
164
+ if (!pkgMatch) return {
165
+ isPure: false,
166
+ package: null
167
+ };
168
+ const pkg = pkgMatch[1];
169
+ const bodyTrimmed = body.replace(/^\s+/, "");
170
+ if (!/^## Dependencies\b/.test(bodyTrimmed)) return {
171
+ isPure: false,
172
+ package: null
173
+ };
174
+ if ((bodyTrimmed.match(/^## /gm) ?? []).length !== 1) return {
175
+ isPure: false,
176
+ package: null
177
+ };
178
+ if (/^# /m.test(bodyTrimmed)) return {
179
+ isPure: false,
180
+ package: null
181
+ };
182
+ return {
183
+ isPure: true,
184
+ package: pkg
185
+ };
186
+ }
187
+ function listChangesetFiles(changesetDir) {
188
+ if (!existsSync(changesetDir)) return [];
189
+ return readdirSync(changesetDir).filter((f) => f.endsWith(".md") && f !== "README.md").map((f) => join(changesetDir, f));
190
+ }
191
+ function findPureDependencyChangesets(changesetDir) {
192
+ const result = [];
193
+ for (const file of listChangesetFiles(changesetDir)) {
194
+ let content;
195
+ try {
196
+ content = readFileSync(file, "utf8");
197
+ } catch {
198
+ continue;
199
+ }
200
+ const detection = isPureDependencyChangeset(content);
201
+ if (detection.isPure && detection.package) result.push({
202
+ file,
203
+ package: detection.package
204
+ });
205
+ }
206
+ return result;
207
+ }
208
+ function findMixedDependencyChangesets(changesetDir) {
209
+ const result = [];
210
+ for (const file of listChangesetFiles(changesetDir)) {
211
+ let content;
212
+ try {
213
+ content = readFileSync(file, "utf8");
214
+ } catch {
215
+ continue;
216
+ }
217
+ if (/^## Dependencies\b/m.test(content) && !isPureDependencyChangeset(content).isPure) result.push(file);
218
+ }
219
+ return result;
220
+ }
221
+ /**
222
+ * Render a single-package, patch-bump changeset for a diff whose rows have
223
+ * already been resolved/filtered by {@link resolveDiffRows}.
224
+ */
225
+ function renderChangesetContent(diff) {
226
+ return `${`---\n"${diff.package}": patch\n---`}\n\n## Dependencies\n\n${serializeDependencyTableToMarkdown([...diff.rows])}\n`;
227
+ }
228
+ const _tag = Context.Tag("Changesets/DepsRegen");
229
+ /**
230
+ * @internal
231
+ */
232
+ const DepsRegenBase = _tag();
233
+ /**
234
+ * Effect service tag for {@link DepsRegenShape}.
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * import { Effect } from "effect";
239
+ * import { Changesets } from "@savvy-web/silk-effects";
240
+ *
241
+ * const program = Effect.gen(function* () {
242
+ * const svc = yield* Changesets.DepsRegen;
243
+ * const plan = yield* svc.plan({ cwd: process.cwd() });
244
+ * return yield* svc.execute(plan);
245
+ * });
246
+ * ```
247
+ *
248
+ * @public
249
+ */
250
+ var DepsRegen = class extends DepsRegenBase {};
251
+ /**
252
+ * Build a {@link DepsRegenShape} that closes over already-resolved service
253
+ * implementations, keeping the public `plan`/`execute` signatures
254
+ * requirement-free (`R = never`).
255
+ */
256
+ function makeShape(reader, inspector, discovery, resolver, detector) {
257
+ const provideResolver = Layer.succeed(CatalogResolver, resolver);
258
+ const provideDetector = Layer.succeed(PublishabilityDetector, detector);
259
+ const plan = (options) => Effect.gen(function* () {
260
+ const resolvedCwd = resolve(options.cwd);
261
+ const changesetDir = join(resolvedCwd, ".changeset");
262
+ let fromRef = options.from;
263
+ if (!fromRef) {
264
+ let baseBranch = options.base;
265
+ if (!baseBranch) baseBranch = (yield* inspector.inspect(resolvedCwd).pipe(Effect.catchTag("ConfigurationError", () => Effect.succeed({ baseBranch: "main" })))).baseBranch;
266
+ fromRef = yield* gitMergeBase(resolvedCwd, baseBranch);
267
+ }
268
+ const rawDiffs = computeWorkspaceDependencyDiffs(yield* reader.snapshotAt(resolvedCwd, fromRef), options.to ? yield* reader.snapshotAt(resolvedCwd, options.to) : snapshotFromWorktree(resolvedCwd));
269
+ const targetPkg = options.package;
270
+ const publishable = yield* listPublishablePackageNames(yield* discovery.listPackages(resolvedCwd)).pipe(Effect.provide(provideDetector));
271
+ const keepDevDeps = options.includeDevDeps === true;
272
+ const scoped = targetPkg ? rawDiffs.filter((d) => d.package === targetPkg) : rawDiffs.filter((d) => publishable.has(d.package));
273
+ const resolved = [];
274
+ for (const diff of scoped) {
275
+ const next = yield* resolveDiffRows(diff, keepDevDeps).pipe(Effect.provide(provideResolver));
276
+ if (next.rows.length > 0) resolved.push(next);
277
+ }
278
+ const existingPure = findPureDependencyChangesets(changesetDir);
279
+ const skippedMixed = findMixedDependencyChangesets(changesetDir);
280
+ const toDelete = targetPkg ? existingPure.filter((p) => p.package === targetPkg) : existingPure.filter((p) => publishable.has(p.package));
281
+ const chosenFilenames = /* @__PURE__ */ new Set();
282
+ return {
283
+ toDelete,
284
+ toWrite: resolved.map((diff) => ({
285
+ file: join(changesetDir, `${randomFilename(changesetDir, chosenFilenames)}.md`),
286
+ package: diff.package,
287
+ diff
288
+ })),
289
+ skippedMixed
290
+ };
291
+ });
292
+ const execute = (plan) => Effect.sync(() => {
293
+ const deleted = [];
294
+ const written = [];
295
+ for (const entry of plan.toWrite) {
296
+ writeFileSync(entry.file, renderChangesetContent(entry.diff));
297
+ written.push(entry.file);
298
+ }
299
+ for (const entry of plan.toDelete) try {
300
+ unlinkSync(entry.file);
301
+ deleted.push(entry.file);
302
+ } catch {}
303
+ return {
304
+ deleted,
305
+ written,
306
+ skippedMixed: plan.skippedMixed
307
+ };
308
+ });
309
+ return {
310
+ plan,
311
+ execute
312
+ };
313
+ }
314
+ /**
315
+ * Live layer for {@link DepsRegen}.
316
+ *
317
+ * Requires {@link WorkspaceSnapshotReader}, {@link ConfigInspector},
318
+ * `WorkspaceDiscovery`, `CatalogResolver`, and `PublishabilityDetector`
319
+ * (the last three from `workspaces-effect`).
320
+ *
321
+ * @public
322
+ */
323
+ const DepsRegenLive = Layer.effect(DepsRegen, Effect.gen(function* () {
324
+ return makeShape(yield* WorkspaceSnapshotReader, yield* ConfigInspector, yield* WorkspaceDiscovery, yield* CatalogResolver, yield* PublishabilityDetector);
325
+ }));
326
+
327
+ //#endregion
328
+ export { DepsRegen, DepsRegenBase, DepsRegenLive, isPureDependencyChangeset, resolveDiffRows };
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context, Data, Effect, Equal, Hash, Layer, Option, Schema, Stream } from "effect";
2
2
  import { Command, CommandExecutor, FileSystem } from "@effect/platform";
3
- import { PackageManagerDetector, PublishConfig, PublishTarget, PublishabilityDetector, TopologicalSorter, WorkspaceDiscovery, WorkspaceDiscoveryError, WorkspacePackage, WorkspaceRoot } from "workspaces-effect";
3
+ import { CatalogResolver, PackageManagerDetector, PublishConfig, PublishTarget, PublishabilityDetector, TopologicalSorter, WorkspaceDiscovery, WorkspaceDiscoveryError, WorkspacePackage, WorkspaceRoot } from "workspaces-effect";
4
4
  import { Plugin } from "unified";
5
5
  import { PlatformError } from "@effect/platform/Error";
6
6
 
@@ -1571,6 +1571,16 @@ declare const DependencyTableTypeSchema: Schema.Literal<["dependency", "devDepen
1571
1571
  * @public
1572
1572
  */
1573
1573
  type DependencyTableType = typeof DependencyTableTypeSchema.Type;
1574
+ /**
1575
+ * The canonical accepted-value pattern for a dependency-table From/To cell:
1576
+ * the em-dash sentinel (U+2014), a bare/`~`/`^` semver, or — as a last-resort
1577
+ * fallback when a `catalog:`/`workspace:` specifier could not be resolved to a
1578
+ * concrete version — a pnpm protocol string. Non-overlapping alternatives keep
1579
+ * this free of polynomial backtracking (CodeQL).
1580
+ *
1581
+ * @public
1582
+ */
1583
+ declare const VERSION_RE: RegExp;
1574
1584
  /**
1575
1585
  * Version string or em dash (U+2014) sentinel for added/removed entries.
1576
1586
  *
@@ -1845,7 +1855,7 @@ declare class DependencyTable {
1845
1855
  /**
1846
1856
  * Class-based API wrapper for changeset linting.
1847
1857
  *
1848
- * Provides a static class interface that runs all remark-lint rules
1858
+ * Provides a static class interface that runs all five remark-lint rules
1849
1859
  * against changeset markdown files and returns structured diagnostics.
1850
1860
  *
1851
1861
  * @internal
@@ -1859,11 +1869,12 @@ declare class DependencyTable {
1859
1869
  * (file, line, column) for integration with editors, CI reporters, and
1860
1870
  * the Effect CLI's `lint` and `check` commands.
1861
1871
  *
1862
- * The four rules that produce lint messages are:
1872
+ * The five rules that produce lint messages are:
1863
1873
  *
1864
1874
  * - **heading-hierarchy** -- ensures headings follow a valid nesting order
1865
1875
  * - **required-sections** -- checks that mandatory sections are present
1866
1876
  * - **content-structure** -- validates the structure of section content
1877
+ * - **dependency-table-format** -- enforces the machine-generated dependency-table format for `## Dependencies` sections
1867
1878
  * - **uncategorized-content** -- flags content outside recognized section headings
1868
1879
  *
1869
1880
  * @public
@@ -1882,9 +1893,10 @@ interface LintMessage {
1882
1893
  * Identifier of the remark-lint rule that produced this message.
1883
1894
  *
1884
1895
  * @remarks
1885
- * Corresponds to one of the four built-in rules: `"heading-hierarchy"`,
1886
- * `"required-sections"`, `"content-structure"`, or `"uncategorized-content"`.
1887
- * Falls back to `"unknown"` if the underlying vfile message has no rule ID.
1896
+ * Corresponds to one of the five built-in rules: `"heading-hierarchy"`,
1897
+ * `"required-sections"`, `"content-structure"`, `"dependency-table-format"`,
1898
+ * or `"uncategorized-content"`. Falls back to `"unknown"` if the underlying
1899
+ * vfile message has no rule ID.
1888
1900
  */
1889
1901
  rule: string;
1890
1902
  /**
@@ -1917,9 +1929,9 @@ interface LintMessage {
1917
1929
  /**
1918
1930
  * Static class for linting changeset markdown files.
1919
1931
  *
1920
- * Runs the four remark-lint rules (heading-hierarchy, required-sections,
1921
- * content-structure, uncategorized-content) against changeset markdown
1922
- * and returns structured {@link LintMessage} diagnostics.
1932
+ * Runs the five remark-lint rules (heading-hierarchy, required-sections,
1933
+ * content-structure, dependency-table-format, uncategorized-content) against
1934
+ * changeset markdown and returns structured {@link LintMessage} diagnostics.
1923
1935
  *
1924
1936
  * @remarks
1925
1937
  * This class implements the pre-validation layer of the three-layer
@@ -2004,7 +2016,7 @@ declare class ChangesetLinter {
2004
2016
  *
2005
2017
  * @remarks
2006
2018
  * Reads the file synchronously, strips YAML frontmatter, and runs all
2007
- * four lint rules. The file path is preserved in each returned
2019
+ * five lint rules. The file path is preserved in each returned
2008
2020
  * {@link LintMessage} for error reporting.
2009
2021
  *
2010
2022
  * @param filePath - Absolute or relative path to the changeset `.md` file
@@ -2015,7 +2027,7 @@ declare class ChangesetLinter {
2015
2027
  * Validate a markdown string directly.
2016
2028
  *
2017
2029
  * @remarks
2018
- * Strips YAML frontmatter (if present) and runs all four lint rules
2030
+ * Strips YAML frontmatter (if present) and runs all five lint rules
2019
2031
  * against the remaining content. This method is useful for validating
2020
2032
  * changeset content that is already in memory, such as in test suites
2021
2033
  * or editor integrations.
@@ -3480,6 +3492,231 @@ declare const ChangelogServiceBase: Context.TagClass<ChangelogService, "Changelo
3480
3492
  */
3481
3493
  declare class ChangelogService extends ChangelogServiceBase {}
3482
3494
  //#endregion
3495
+ //#region src/changesets/services/workspace-snapshot.d.ts
3496
+ /**
3497
+ * One workspace package as it existed at a specific git ref.
3498
+ *
3499
+ * @public
3500
+ */
3501
+ interface WorkspaceSnapshot {
3502
+ /** Package name from `package.json#name`. */
3503
+ readonly name: string;
3504
+ /** Repo-relative path of the package directory at this ref. */
3505
+ readonly relativePath: string;
3506
+ /** Package version from `package.json#version`. */
3507
+ readonly version: string;
3508
+ /** Declared `dependencies` (raw strings, including `workspace:` / `catalog:` protocols). */
3509
+ readonly dependencies: Readonly<Record<string, string>>;
3510
+ /** Declared `devDependencies`. */
3511
+ readonly devDependencies: Readonly<Record<string, string>>;
3512
+ /** Declared `peerDependencies`. */
3513
+ readonly peerDependencies: Readonly<Record<string, string>>;
3514
+ /** Declared `optionalDependencies`. */
3515
+ readonly optionalDependencies: Readonly<Record<string, string>>;
3516
+ }
3517
+ /**
3518
+ * Effect service interface for reading workspace snapshots.
3519
+ *
3520
+ * @public
3521
+ */
3522
+ interface WorkspaceSnapshotReaderShape {
3523
+ /**
3524
+ * Read every workspace package's snapshot at the given git ref.
3525
+ *
3526
+ * @param cwd - Project root (must be inside a git repo)
3527
+ * @param ref - Any valid git revision spec — branch, tag, SHA, `HEAD~1`, etc.
3528
+ * @returns Effect resolving to one {@link WorkspaceSnapshot} per workspace
3529
+ * package present at that ref, or failing with {@link GitError}
3530
+ */
3531
+ readonly snapshotAt: (cwd: string, ref: string) => Effect.Effect<ReadonlyArray<WorkspaceSnapshot>, GitError>;
3532
+ }
3533
+ /**
3534
+ * @internal
3535
+ */
3536
+ declare const WorkspaceSnapshotReaderBase: Context.TagClass<WorkspaceSnapshotReader, "WorkspaceSnapshotReader", WorkspaceSnapshotReaderShape>;
3537
+ /**
3538
+ * Effect service tag for {@link WorkspaceSnapshotReaderShape}.
3539
+ *
3540
+ * @public
3541
+ */
3542
+ declare class WorkspaceSnapshotReader extends WorkspaceSnapshotReaderBase {}
3543
+ /**
3544
+ * Production layer for {@link WorkspaceSnapshotReader}.
3545
+ *
3546
+ * @public
3547
+ */
3548
+ declare const WorkspaceSnapshotReaderLive: Layer.Layer<WorkspaceSnapshotReader>;
3549
+ //#endregion
3550
+ //#region src/changesets/utils/dep-diff.d.ts
3551
+ /**
3552
+ * A workspace package's worth of dependency-table rows.
3553
+ *
3554
+ * @public
3555
+ */
3556
+ interface WorkspaceDependencyDiff {
3557
+ /** The workspace package whose `package.json` changed. */
3558
+ readonly package: string;
3559
+ /** Repo-relative path of the package directory (taken from the `after` snapshot when available). */
3560
+ readonly relativePath: string;
3561
+ /** One row per dependency change, sorted by the existing `sortDependencyRows` convention. */
3562
+ readonly rows: ReadonlyArray<DependencyTableRow>;
3563
+ }
3564
+ /**
3565
+ * Diff two workspace snapshots and return per-package dependency-table rows.
3566
+ *
3567
+ * @param before - Snapshot at the older ref (typically the merge base). Pass
3568
+ * `null` for workspace packages that did not exist at the older ref — every
3569
+ * declared dep is then reported as `"added"`.
3570
+ * @param after - Snapshot at the newer ref (typically the working tree).
3571
+ * @returns One {@link WorkspaceDependencyDiff} entry per workspace package
3572
+ * that has at least one row. Packages with no changes are omitted.
3573
+ *
3574
+ * @public
3575
+ */
3576
+ declare function computeWorkspaceDependencyDiffs(beforeSnapshots: ReadonlyArray<WorkspaceSnapshot>, afterSnapshots: ReadonlyArray<WorkspaceSnapshot>): ReadonlyArray<WorkspaceDependencyDiff>;
3577
+ //#endregion
3578
+ //#region src/changesets/services/deps-regen.d.ts
3579
+ /**
3580
+ * Resolve protocol From/To cells to concrete versions (raw-string fallback
3581
+ * when unresolved or on resolver error), leave em-dash sentinels untouched,
3582
+ * then optionally drop `devDependency` rows, and re-sort.
3583
+ *
3584
+ * @param diff - One workspace package's dependency-table rows.
3585
+ * @param keepDevDeps - When `true`, retain `devDependency` rows; otherwise
3586
+ * drop them unconditionally (the regen default).
3587
+ * @returns An Effect yielding the transformed {@link WorkspaceDependencyDiff}.
3588
+ *
3589
+ * @public
3590
+ */
3591
+ declare const resolveDiffRows: (diff: WorkspaceDependencyDiff, keepDevDeps?: boolean) => Effect.Effect<WorkspaceDependencyDiff, never, CatalogResolver>;
3592
+ /**
3593
+ * Strict detection of "pure dependency changesets" per the documented
3594
+ * rules: single-package frontmatter, single `## Dependencies` heading,
3595
+ * no other body content beyond that section.
3596
+ *
3597
+ * @param content - Raw `.changeset/*.md` file contents.
3598
+ * @returns `{ isPure, package }` — `isPure` is `true` only for a
3599
+ * single-package, Dependencies-only changeset; `package` is the sole
3600
+ * frontmatter package name (or `null` when not pure).
3601
+ *
3602
+ * @public
3603
+ */
3604
+ declare function isPureDependencyChangeset(content: string): {
3605
+ isPure: boolean;
3606
+ package: string | null;
3607
+ };
3608
+ /**
3609
+ * A complete, side-effect-free regen plan: which stale pure-dependency
3610
+ * changesets to delete, which fresh changesets to write (carrying the
3611
+ * already-resolved diff), and which mixed changesets were left untouched.
3612
+ *
3613
+ * @public
3614
+ */
3615
+ interface RegenPlan {
3616
+ readonly toDelete: ReadonlyArray<{
3617
+ readonly file: string;
3618
+ readonly package: string;
3619
+ }>;
3620
+ readonly toWrite: ReadonlyArray<{
3621
+ readonly file: string;
3622
+ readonly package: string;
3623
+ readonly diff: WorkspaceDependencyDiff;
3624
+ }>;
3625
+ readonly skippedMixed: ReadonlyArray<string>;
3626
+ }
3627
+ /**
3628
+ * The result of applying a {@link RegenPlan}: the files actually deleted
3629
+ * and written, plus the mixed changesets that were skipped.
3630
+ *
3631
+ * @public
3632
+ */
3633
+ interface RegenResult {
3634
+ readonly deleted: ReadonlyArray<string>;
3635
+ readonly written: ReadonlyArray<string>;
3636
+ readonly skippedMixed: ReadonlyArray<string>;
3637
+ }
3638
+ /**
3639
+ * Options for {@link DepsRegenShape.plan}.
3640
+ *
3641
+ * @public
3642
+ */
3643
+ interface DepsRegenOptions {
3644
+ /** Project root (containing `.changeset/`). */
3645
+ readonly cwd: string;
3646
+ /** Override the base branch used to compute the merge-base when `from` is omitted. */
3647
+ readonly base?: string;
3648
+ /** Restrict regeneration to a single workspace package. */
3649
+ readonly package?: string;
3650
+ /**
3651
+ * When `true`, retain `devDependency` rows (the `deps detect` path);
3652
+ * when falsy (the `deps regen` default), drop them unconditionally.
3653
+ * Protocol resolution runs regardless.
3654
+ */
3655
+ readonly includeDevDeps?: boolean;
3656
+ /**
3657
+ * Older ref to diff from. Defaults to `git merge-base <base branch> HEAD`.
3658
+ */
3659
+ readonly from?: string;
3660
+ /**
3661
+ * Newer ref to diff to. Defaults to the working tree (staged + unstaged
3662
+ * + untracked) via {@link snapshotFromWorktree}.
3663
+ */
3664
+ readonly to?: string;
3665
+ }
3666
+ /**
3667
+ * Effect service interface for the deps regen/detect orchestration.
3668
+ *
3669
+ * @public
3670
+ */
3671
+ interface DepsRegenShape {
3672
+ /**
3673
+ * Compute a complete {@link RegenPlan} without touching the filesystem.
3674
+ *
3675
+ * @param options - See {@link DepsRegenOptions}.
3676
+ * @returns An Effect yielding the plan, or failing with {@link GitError}.
3677
+ */
3678
+ readonly plan: (options: DepsRegenOptions) => Effect.Effect<RegenPlan, GitError | WorkspaceDiscoveryError, never>;
3679
+ /**
3680
+ * Apply a {@link RegenPlan}: delete stale changesets, write fresh ones.
3681
+ *
3682
+ * @param plan - The plan produced by {@link DepsRegenShape.plan}.
3683
+ * @returns An Effect yielding a {@link RegenResult}.
3684
+ */
3685
+ readonly execute: (plan: RegenPlan) => Effect.Effect<RegenResult, never, never>;
3686
+ }
3687
+ /**
3688
+ * @internal
3689
+ */
3690
+ declare const DepsRegenBase: Context.TagClass<DepsRegen, "Changesets/DepsRegen", DepsRegenShape>;
3691
+ /**
3692
+ * Effect service tag for {@link DepsRegenShape}.
3693
+ *
3694
+ * @example
3695
+ * ```typescript
3696
+ * import { Effect } from "effect";
3697
+ * import { Changesets } from "@savvy-web/silk-effects";
3698
+ *
3699
+ * const program = Effect.gen(function* () {
3700
+ * const svc = yield* Changesets.DepsRegen;
3701
+ * const plan = yield* svc.plan({ cwd: process.cwd() });
3702
+ * return yield* svc.execute(plan);
3703
+ * });
3704
+ * ```
3705
+ *
3706
+ * @public
3707
+ */
3708
+ declare class DepsRegen extends DepsRegenBase {}
3709
+ /**
3710
+ * Live layer for {@link DepsRegen}.
3711
+ *
3712
+ * Requires {@link WorkspaceSnapshotReader}, {@link ConfigInspector},
3713
+ * `WorkspaceDiscovery`, `CatalogResolver`, and `PublishabilityDetector`
3714
+ * (the last three from `workspaces-effect`).
3715
+ *
3716
+ * @public
3717
+ */
3718
+ declare const DepsRegenLive: Layer.Layer<DepsRegen, never, WorkspaceSnapshotReader | ConfigInspector | WorkspaceDiscovery | CatalogResolver | PublishabilityDetector>;
3719
+ //#endregion
3483
3720
  //#region src/changesets/schemas/release-plan.d.ts
3484
3721
  /** A semantic-version bump level (the `"none"` plan type is filtered out upstream). @public */
3485
3722
  declare const BumpTypeSchema: Schema.Literal<["major", "minor", "patch"]>;
@@ -3594,61 +3831,6 @@ declare function makeReleasePlannerTest(fixed: {
3594
3831
  readonly apply?: AppliedRelease;
3595
3832
  }): Layer.Layer<ReleasePlanner>;
3596
3833
  //#endregion
3597
- //#region src/changesets/services/workspace-snapshot.d.ts
3598
- /**
3599
- * One workspace package as it existed at a specific git ref.
3600
- *
3601
- * @public
3602
- */
3603
- interface WorkspaceSnapshot {
3604
- /** Package name from `package.json#name`. */
3605
- readonly name: string;
3606
- /** Repo-relative path of the package directory at this ref. */
3607
- readonly relativePath: string;
3608
- /** Package version from `package.json#version`. */
3609
- readonly version: string;
3610
- /** Declared `dependencies` (raw strings, including `workspace:` / `catalog:` protocols). */
3611
- readonly dependencies: Readonly<Record<string, string>>;
3612
- /** Declared `devDependencies`. */
3613
- readonly devDependencies: Readonly<Record<string, string>>;
3614
- /** Declared `peerDependencies`. */
3615
- readonly peerDependencies: Readonly<Record<string, string>>;
3616
- /** Declared `optionalDependencies`. */
3617
- readonly optionalDependencies: Readonly<Record<string, string>>;
3618
- }
3619
- /**
3620
- * Effect service interface for reading workspace snapshots.
3621
- *
3622
- * @public
3623
- */
3624
- interface WorkspaceSnapshotReaderShape {
3625
- /**
3626
- * Read every workspace package's snapshot at the given git ref.
3627
- *
3628
- * @param cwd - Project root (must be inside a git repo)
3629
- * @param ref - Any valid git revision spec — branch, tag, SHA, `HEAD~1`, etc.
3630
- * @returns Effect resolving to one {@link WorkspaceSnapshot} per workspace
3631
- * package present at that ref, or failing with {@link GitError}
3632
- */
3633
- readonly snapshotAt: (cwd: string, ref: string) => Effect.Effect<ReadonlyArray<WorkspaceSnapshot>, GitError>;
3634
- }
3635
- /**
3636
- * @internal
3637
- */
3638
- declare const WorkspaceSnapshotReaderBase: Context.TagClass<WorkspaceSnapshotReader, "WorkspaceSnapshotReader", WorkspaceSnapshotReaderShape>;
3639
- /**
3640
- * Effect service tag for {@link WorkspaceSnapshotReaderShape}.
3641
- *
3642
- * @public
3643
- */
3644
- declare class WorkspaceSnapshotReader extends WorkspaceSnapshotReaderBase {}
3645
- /**
3646
- * Production layer for {@link WorkspaceSnapshotReader}.
3647
- *
3648
- * @public
3649
- */
3650
- declare const WorkspaceSnapshotReaderLive: Layer.Layer<WorkspaceSnapshotReader>;
3651
- //#endregion
3652
3834
  //#region src/changesets/schemas/changeset.d.ts
3653
3835
  /**
3654
3836
  * Schema for a changeset summary (1--1000 characters).
@@ -4290,34 +4472,6 @@ declare const LegacyVersionFilesSchema: Schema.Array$<Schema.Struct<{
4290
4472
  package: Schema.optional<Schema.filter<typeof Schema.String>>;
4291
4473
  }>>;
4292
4474
  //#endregion
4293
- //#region src/changesets/utils/dep-diff.d.ts
4294
- /**
4295
- * A workspace package's worth of dependency-table rows.
4296
- *
4297
- * @public
4298
- */
4299
- interface WorkspaceDependencyDiff {
4300
- /** The workspace package whose `package.json` changed. */
4301
- readonly package: string;
4302
- /** Repo-relative path of the package directory (taken from the `after` snapshot when available). */
4303
- readonly relativePath: string;
4304
- /** One row per dependency change, sorted by the existing `sortDependencyRows` convention. */
4305
- readonly rows: ReadonlyArray<DependencyTableRow>;
4306
- }
4307
- /**
4308
- * Diff two workspace snapshots and return per-package dependency-table rows.
4309
- *
4310
- * @param before - Snapshot at the older ref (typically the merge base). Pass
4311
- * `null` for workspace packages that did not exist at the older ref — every
4312
- * declared dep is then reported as `"added"`.
4313
- * @param after - Snapshot at the newer ref (typically the working tree).
4314
- * @returns One {@link WorkspaceDependencyDiff} entry per workspace package
4315
- * that has at least one row. Packages with no changes are omitted.
4316
- *
4317
- * @public
4318
- */
4319
- declare function computeWorkspaceDependencyDiffs(beforeSnapshots: ReadonlyArray<WorkspaceSnapshot>, afterSnapshots: ReadonlyArray<WorkspaceSnapshot>): ReadonlyArray<WorkspaceDependencyDiff>;
4320
- //#endregion
4321
4475
  //#region src/changesets/utils/dependency-table.d.ts
4322
4476
  /**
4323
4477
  * Serialize dependency table rows to a markdown table string.
@@ -5262,7 +5416,7 @@ declare const RequiredSectionsRule: import("unified-lint-rule").Plugin<Root, unk
5262
5416
  //#region src/changesets/remark/rules/uncategorized-content.d.ts
5263
5417
  declare const UncategorizedContentRule: import("unified-lint-rule").Plugin<Root, unknown>;
5264
5418
  declare namespace index_d_exports {
5265
- export { AggregateDependencyTablesPlugin, AppliedRelease, AppliedReleaseEntrySchema, AppliedReleaseSchema, BranchAnalysis, BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchAnalyzerShape, BranchFileEntry, BranchFileEntrySchema, BumpType, BumpTypeSchema, Categories, Changelog, ChangelogService, ChangelogServiceBase, ChangelogServiceShape, ChangelogTransformer, Changeset, ChangesetLinter, ChangesetOptions, ChangesetOptionsSchema, ChangesetPreview, ChangesetPreviewSchema, ChangesetSchema, ChangesetSummarySchema, ChangesetValidationError, ChangesetValidationErrorBase, Classification, ClassificationReason, ClassificationReasonSchema, ClassificationSchema, CommitHashSchema, ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, ConfigInspectorShape, ConfigurationError, ConfigurationErrorBase, ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, DependencyAction, DependencyActionSchema, DependencyTable, DependencyTableFormatRule, DependencyTableRow, DependencyTableRowSchema, DependencyTableSchema, DependencyTableType, DependencyTableTypeSchema, DependencyType, DependencyTypeSchema, DependencyUpdate, DependencyUpdateSchema, FileStatus, FileStatusSchema, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, GitHubCommitInfo, GitHubInfo, GitHubInfoSchema, GitHubLive, GitHubService, GitHubServiceBase, GitHubServiceShape, GlobSchema, HeadingHierarchyRule, InspectedConfig, InspectedConfigSchema, IssueLinkRefsPlugin, IssueNumberSchema, JsonPathSchema, LegacyVersionFileConfig, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, LintMessage, MarkdownLive, MarkdownParseError, MarkdownParseErrorBase, MarkdownService, MarkdownServiceBase, MarkdownServiceShape, ContentStructureRule$1 as MarkdownlintContentStructureRule, DependencyTableFormatRule$1 as MarkdownlintDependencyTableFormatRule, HeadingHierarchyRule$1 as MarkdownlintHeadingHierarchyRule, RequiredSectionsRule$1 as MarkdownlintRequiredSectionsRule, UncategorizedContentRule$1 as MarkdownlintUncategorizedContentRule, MergeSectionsPlugin, NonEmptyString, NormalizeFormatPlugin, PackageScope, PackageScopeSchema, PackagesRecordSchema, PendingChangeset, PendingChangesetSchema, PositiveInteger, PreviewRelease, PreviewReleaseSchema, ReleasePlanError, ReleasePlanErrorBase, ReleasePlanner, ReleasePlannerBase, ReleasePlannerLive, ReleasePlannerShape, ReorderSectionsPlugin, RepoSchema, RequiredSectionsRule, ResolvedPackageScope, ResolvedPackageScopeSchema, ResolvedVersionFile, ResolvedVersionFileSchema, SectionCategory, SectionCategorySchema, SilkChangesetPreset, SilkChangesetTransformPreset, SilkChangesetsRules, UncategorizedContentRule, UrlOrMarkdownLinkSchema, UsernameSchema, VersionFileConfig, VersionFileConfigSchema, VersionFileError, VersionFileErrorBase, VersionFileUpdate, VersionFileUpdateRecordSchema, VersionFiles, VersionFilesSchema, VersionOrEmptySchema, VersionType, VersionTypeSchema, WorkspaceDependencyDiff, WorkspaceSnapshot, WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive, WorkspaceSnapshotReaderShape, WorkspaceVersion, changelogFunctions, computeWorkspaceDependencyDiffs, gitMergeBase, listPublishablePackageNames, makeBranchAnalyzerTest, makeConfigInspectorTest, makeGitHubTest, makeReleasePlannerTest, serializeDependencyTableToMarkdown, snapshotFromWorktree };
5419
+ export { AggregateDependencyTablesPlugin, AppliedRelease, AppliedReleaseEntrySchema, AppliedReleaseSchema, BranchAnalysis, BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchAnalyzerShape, BranchFileEntry, BranchFileEntrySchema, BumpType, BumpTypeSchema, Categories, Changelog, ChangelogService, ChangelogServiceBase, ChangelogServiceShape, ChangelogTransformer, Changeset, ChangesetLinter, ChangesetOptions, ChangesetOptionsSchema, ChangesetPreview, ChangesetPreviewSchema, ChangesetSchema, ChangesetSummarySchema, ChangesetValidationError, ChangesetValidationErrorBase, Classification, ClassificationReason, ClassificationReasonSchema, ClassificationSchema, CommitHashSchema, ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, ConfigInspectorShape, ConfigurationError, ConfigurationErrorBase, ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, DependencyAction, DependencyActionSchema, DependencyTable, DependencyTableFormatRule, DependencyTableRow, DependencyTableRowSchema, DependencyTableSchema, DependencyTableType, DependencyTableTypeSchema, DependencyType, DependencyTypeSchema, DependencyUpdate, DependencyUpdateSchema, DepsRegen, DepsRegenBase, DepsRegenLive, DepsRegenOptions, DepsRegenShape, FileStatus, FileStatusSchema, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, GitHubCommitInfo, GitHubInfo, GitHubInfoSchema, GitHubLive, GitHubService, GitHubServiceBase, GitHubServiceShape, GlobSchema, HeadingHierarchyRule, InspectedConfig, InspectedConfigSchema, IssueLinkRefsPlugin, IssueNumberSchema, JsonPathSchema, LegacyVersionFileConfig, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, LintMessage, MarkdownLive, MarkdownParseError, MarkdownParseErrorBase, MarkdownService, MarkdownServiceBase, MarkdownServiceShape, ContentStructureRule$1 as MarkdownlintContentStructureRule, DependencyTableFormatRule$1 as MarkdownlintDependencyTableFormatRule, HeadingHierarchyRule$1 as MarkdownlintHeadingHierarchyRule, RequiredSectionsRule$1 as MarkdownlintRequiredSectionsRule, UncategorizedContentRule$1 as MarkdownlintUncategorizedContentRule, MergeSectionsPlugin, NonEmptyString, NormalizeFormatPlugin, PackageScope, PackageScopeSchema, PackagesRecordSchema, PendingChangeset, PendingChangesetSchema, PositiveInteger, PreviewRelease, PreviewReleaseSchema, RegenPlan, RegenResult, ReleasePlanError, ReleasePlanErrorBase, ReleasePlanner, ReleasePlannerBase, ReleasePlannerLive, ReleasePlannerShape, ReorderSectionsPlugin, RepoSchema, RequiredSectionsRule, ResolvedPackageScope, ResolvedPackageScopeSchema, ResolvedVersionFile, ResolvedVersionFileSchema, SectionCategory, SectionCategorySchema, SilkChangesetPreset, SilkChangesetTransformPreset, SilkChangesetsRules, UncategorizedContentRule, UrlOrMarkdownLinkSchema, UsernameSchema, VERSION_RE, VersionFileConfig, VersionFileConfigSchema, VersionFileError, VersionFileErrorBase, VersionFileUpdate, VersionFileUpdateRecordSchema, VersionFiles, VersionFilesSchema, VersionOrEmptySchema, VersionType, VersionTypeSchema, WorkspaceDependencyDiff, WorkspaceSnapshot, WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive, WorkspaceSnapshotReaderShape, WorkspaceVersion, changelogFunctions, computeWorkspaceDependencyDiffs, gitMergeBase, isPureDependencyChangeset, listPublishablePackageNames, makeBranchAnalyzerTest, makeConfigInspectorTest, makeGitHubTest, makeReleasePlannerTest, resolveDiffRows, serializeDependencyTableToMarkdown, snapshotFromWorktree };
5266
5420
  }
5267
5421
  //#endregion
5268
5422
  //#region src/commitlint/config/schema.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/silk-effects",
3
- "version": "1.5.2",
3
+ "version": "1.6.0",
4
4
  "private": false,
5
5
  "description": "Shared Effect library for Silk Suite conventions",
6
6
  "homepage": "https://github.com/savvy-web/systems/tree/main/packages/silk-effects",
@@ -33,14 +33,14 @@
33
33
  "@changesets/get-github-info": "^0.8.0",
34
34
  "@changesets/get-release-plan": "^4.0.16",
35
35
  "@manypkg/get-packages": "^1.1.3",
36
- "jsonc-effect": "^0.2.1",
36
+ "jsonc-effect": "^0.3.0",
37
37
  "mdast-util-heading-range": "^4.0.0",
38
38
  "mdast-util-to-string": "^4.0.0",
39
39
  "prettier": "^3.8.4",
40
40
  "remark-gfm": "^4.0.1",
41
41
  "remark-parse": "^11.0.0",
42
42
  "remark-stringify": "^11.0.0",
43
- "semver-effect": "^0.2.1",
43
+ "semver-effect": "^0.3.0",
44
44
  "shell-quote": "^1.9.0",
45
45
  "sort-package-json": "^4.0.0",
46
46
  "tinyglobby": "^0.2.17",
@@ -49,7 +49,7 @@
49
49
  "unist-util-visit": "^5.1.0",
50
50
  "workspaces-effect": "^1.2.0",
51
51
  "yaml": "^2.9.0",
52
- "yaml-effect": "^0.6.0",
52
+ "yaml-effect": "^0.7.0",
53
53
  "yaml-lint": "^1.7.0"
54
54
  },
55
55
  "peerDependencies": {