@savvy-web/silk-effects 1.6.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -300,6 +300,49 @@ var GitError = class extends GitErrorBase {
300
300
  }
301
301
  };
302
302
  /**
303
+ * Base class for {@link ChangesetIOError}.
304
+ *
305
+ * @privateRemarks
306
+ * Effect's `Data.TaggedError` creates an anonymous base class that
307
+ * api-extractor cannot follow without an explicit export.
308
+ *
309
+ * @internal
310
+ */
311
+ const ChangesetIOErrorBase = Data.TaggedError("ChangesetIOError");
312
+ /**
313
+ * Changeset file I/O failure.
314
+ *
315
+ * @remarks
316
+ * Raised by {@link DepsRegen} when reading, writing, listing, or deleting
317
+ * `.changeset/*.md` files fails. Deletion failures during
318
+ * {@link DepsRegenShape.execute} are tolerated (stale changesets are
319
+ * skip-and-continue, so an interrupted run stays safely re-runnable);
320
+ * read, list, and write failures are loud.
321
+ *
322
+ * @example
323
+ * ```typescript
324
+ * import { Effect } from "effect";
325
+ * import { ChangesetIOError } from "@savvy-web/changesets";
326
+ *
327
+ * declare const program: Effect.Effect<void, ChangesetIOError>;
328
+ *
329
+ * const handled = program.pipe(
330
+ * Effect.catchTag("ChangesetIOError", (err) =>
331
+ * Effect.logError(`changeset ${err.operation} failed at ${err.path}: ${err.reason}`)
332
+ * ),
333
+ * );
334
+ * ```
335
+ *
336
+ * @see {@link DepsRegen} which produces these errors during plan/execute
337
+ *
338
+ * @public
339
+ */
340
+ var ChangesetIOError = class extends ChangesetIOErrorBase {
341
+ get message() {
342
+ return `changeset ${this.operation} failed at ${this.path}: ${this.reason}`;
343
+ }
344
+ };
345
+ /**
303
346
  * Base class for {@link ReleasePlanError}.
304
347
  *
305
348
  * @privateRemarks
@@ -325,4 +368,4 @@ var ReleasePlanError = class extends ReleasePlanErrorBase {
325
368
  };
326
369
 
327
370
  //#endregion
328
- export { ChangesetValidationError, ChangesetValidationErrorBase, ConfigurationError, ConfigurationErrorBase, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, MarkdownParseError, MarkdownParseErrorBase, ReleasePlanError, ReleasePlanErrorBase, VersionFileError, VersionFileErrorBase };
371
+ export { ChangesetIOError, ChangesetIOErrorBase, ChangesetValidationError, ChangesetValidationErrorBase, ConfigurationError, ConfigurationErrorBase, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, MarkdownParseError, MarkdownParseErrorBase, ReleasePlanError, ReleasePlanErrorBase, VersionFileError, VersionFileErrorBase };
@@ -1,6 +1,6 @@
1
1
  import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
2
  import { Categories } from "./api/categories.js";
3
- import { ChangesetValidationError, ChangesetValidationErrorBase, ConfigurationError, ConfigurationErrorBase, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, MarkdownParseError, MarkdownParseErrorBase, ReleasePlanError, ReleasePlanErrorBase, VersionFileError, VersionFileErrorBase } from "./errors.js";
3
+ import { ChangesetIOError, ChangesetIOErrorBase, ChangesetValidationError, ChangesetValidationErrorBase, ConfigurationError, ConfigurationErrorBase, GitError, GitErrorBase, GitHubApiError, GitHubApiErrorBase, MarkdownParseError, MarkdownParseErrorBase, ReleasePlanError, ReleasePlanErrorBase, VersionFileError, VersionFileErrorBase } from "./errors.js";
4
4
  import { JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, VersionFileConfigSchema, VersionFilesSchema } from "./schemas/version-files.js";
5
5
  import { GlobSchema, PackageScopeSchema, PackagesRecordSchema } from "./schemas/package-scope.js";
6
6
  import { ChangesetOptionsSchema, RepoSchema } from "./schemas/options.js";
@@ -30,10 +30,9 @@ import { ClassificationReasonSchema, ClassificationSchema, ConfigInspector, Conf
30
30
  import { BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchFileEntrySchema, FileStatusSchema, makeBranchAnalyzerTest } from "./services/branch-analyzer.js";
31
31
  import { ChangelogService, ChangelogServiceBase } from "./services/changelog.js";
32
32
  import { computeWorkspaceDependencyDiffs } from "./utils/dep-diff.js";
33
+ import { gitMergeBase } from "./utils/git.js";
33
34
  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";
35
+ import { DepsRegen, DepsRegenBase, DepsRegenDefault, DepsRegenLive, isPureDependencyChangeset } from "./services/deps-regen.js";
37
36
  import { VersionFiles } from "./utils/version-files.js";
38
37
  import { ReleasePlanner, ReleasePlannerBase, ReleasePlannerLive, makeReleasePlannerTest } from "./services/release-planner.js";
39
38
  import { SectionCategorySchema } from "./categories/types.js";
@@ -65,6 +64,8 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
65
64
  ChangelogService: () => ChangelogService,
66
65
  ChangelogServiceBase: () => ChangelogServiceBase,
67
66
  ChangelogTransformer: () => ChangelogTransformer,
67
+ ChangesetIOError: () => ChangesetIOError,
68
+ ChangesetIOErrorBase: () => ChangesetIOErrorBase,
68
69
  ChangesetLinter: () => ChangesetLinter,
69
70
  ChangesetOptionsSchema: () => ChangesetOptionsSchema,
70
71
  ChangesetPreviewSchema: () => ChangesetPreviewSchema,
@@ -93,6 +94,7 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
93
94
  DependencyUpdateSchema: () => DependencyUpdateSchema,
94
95
  DepsRegen: () => DepsRegen,
95
96
  DepsRegenBase: () => DepsRegenBase,
97
+ DepsRegenDefault: () => DepsRegenDefault,
96
98
  DepsRegenLive: () => DepsRegenLive,
97
99
  FileStatusSchema: () => FileStatusSchema,
98
100
  GitError: () => GitError,
@@ -155,9 +157,6 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
155
157
  VersionFilesSchema: () => VersionFilesSchema,
156
158
  VersionOrEmptySchema: () => VersionOrEmptySchema,
157
159
  VersionTypeSchema: () => VersionTypeSchema,
158
- WorkspaceSnapshotReader: () => WorkspaceSnapshotReader,
159
- WorkspaceSnapshotReaderBase: () => WorkspaceSnapshotReaderBase,
160
- WorkspaceSnapshotReaderLive: () => WorkspaceSnapshotReaderLive,
161
160
  changelogFunctions: () => changelogFunctions,
162
161
  computeWorkspaceDependencyDiffs: () => computeWorkspaceDependencyDiffs,
163
162
  gitMergeBase: () => gitMergeBase,
@@ -167,10 +166,8 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
167
166
  makeConfigInspectorTest: () => makeConfigInspectorTest,
168
167
  makeGitHubTest: () => makeGitHubTest,
169
168
  makeReleasePlannerTest: () => makeReleasePlannerTest,
170
- resolveDiffRows: () => resolveDiffRows,
171
- serializeDependencyTableToMarkdown: () => serializeDependencyTableToMarkdown,
172
- snapshotFromWorktree: () => snapshotFromWorktree
169
+ serializeDependencyTableToMarkdown: () => serializeDependencyTableToMarkdown
173
170
  });
174
171
 
175
172
  //#endregion
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 };
173
+ export { AggregateDependencyTablesPlugin, AppliedReleaseEntrySchema, AppliedReleaseSchema, BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchFileEntrySchema, BumpTypeSchema, Categories, Changelog, ChangelogService, ChangelogServiceBase, ChangelogTransformer, ChangesetIOError, ChangesetIOErrorBase, 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, DepsRegenDefault, 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, changelogFunctions, changesets_exports, computeWorkspaceDependencyDiffs, gitMergeBase, isPureDependencyChangeset, listPublishablePackageNames, makeBranchAnalyzerTest, makeConfigInspectorTest, makeGitHubTest, makeReleasePlannerTest, serializeDependencyTableToMarkdown };
@@ -1,13 +1,16 @@
1
+ import { ChangesetIOError } from "../errors.js";
1
2
  import { serializeDependencyTableToMarkdown, sortDependencyRows } from "../utils/dependency-table.js";
2
- import { ConfigInspector } from "./config-inspector.js";
3
+ import { ChangesetConfigReaderLive } from "../../services/ChangesetConfigReader.js";
4
+ import { ChangesetConfig, ChangesetConfigLive } from "../../services/ChangesetConfig.js";
5
+ import { PublishabilityDetectorAdaptiveLive } from "../../services/SilkPublishability.js";
6
+ import { ConfigInspector, ConfigInspectorLive } from "./config-inspector.js";
3
7
  import { computeWorkspaceDependencyDiffs } from "../utils/dep-diff.js";
8
+ import { gitMergeBase } from "../utils/git.js";
4
9
  import { listPublishablePackageNames } from "../utils/publishability.js";
5
- import { gitMergeBase, snapshotFromWorktree } from "../utils/worktree-snapshot.js";
6
- import { WorkspaceSnapshotReader } from "./workspace-snapshot.js";
7
10
  import { Context, Effect, Layer, Option } from "effect";
8
- import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
9
11
  import { join, resolve } from "node:path";
10
- import { CatalogResolver, PublishabilityDetector, WorkspaceDiscovery } from "workspaces-effect";
12
+ import { FileSystem } from "@effect/platform";
13
+ import { PointInTimeWorkspace, PointInTimeWorkspaceLive, PublishabilityDetector, WorkspaceDiscovery, WorkspaceDiscoveryLive, WorkspaceRootLive } from "workspaces-effect";
11
14
 
12
15
  //#region src/changesets/services/deps-regen.ts
13
16
  /**
@@ -17,9 +20,11 @@ import { CatalogResolver, PublishabilityDetector, WorkspaceDiscovery } from "wor
17
20
  *
18
21
  * @remarks
19
22
  * `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
+ * tree by default, or between two explicit refs) by snapshotting both
24
+ * sides through `PointInTimeWorkspace`, which resolves `catalog:` /
25
+ * `workspace:` specifiers against each ref's own catalogs and package
26
+ * versions before diffing. It drops `devDependency` rows (unless
27
+ * `includeDevDeps`), and returns a complete {@link RegenPlan}
23
28
  * (target filenames + stale-changeset deletes) WITHOUT touching the
24
29
  * filesystem. `execute()` applies a plan — writing the fresh changesets
25
30
  * first, then deleting the stale pure-dependency ones (so an interrupted
@@ -32,41 +37,6 @@ import { CatalogResolver, PublishabilityDetector, WorkspaceDiscovery } from "wor
32
37
  * @see {@link DepsRegenLive} for the production layer
33
38
  *
34
39
  */
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
40
  const ADJECTIVES = [
71
41
  "brave",
72
42
  "clever",
@@ -110,30 +80,35 @@ function pickRandomTriplet() {
110
80
  * Pick a `<adjective>-<noun>-<verb>` filename slug that does not collide
111
81
  * with an existing `.changeset/*.md` OR with a slug already claimed
112
82
  * 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.
83
+ * never writes to disk, so an on-disk existence check alone cannot see
84
+ * slugs chosen moments earlier in the same call — the `chosen` set closes
85
+ * that gap. The triplet space is 1,000 combinations, so a busy repo can
86
+ * plausibly exhaust it across runs; fall back to a timestamp suffix after
87
+ * 20 unlucky picks.
118
88
  *
119
- * @param changesetDir - Directory checked via `existsSync` for on-disk collisions.
89
+ * @param fileExists - Effectful on-disk existence check (never fails; a
90
+ * filesystem error is treated as "does not exist" so filename selection
91
+ * degrades gracefully rather than blocking the plan).
92
+ * @param changesetDir - Directory checked for on-disk collisions.
120
93
  * @param chosen - Basenames (without extension) already picked within this plan;
121
94
  * the picked candidate is added to this set before returning.
122
95
  * @internal
123
96
  */
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;
97
+ function randomFilename(fileExists, changesetDir, chosen) {
98
+ return Effect.gen(function* () {
99
+ for (let i = 0; i < 20; i++) {
100
+ const candidate = pickRandomTriplet();
101
+ if (!chosen.has(candidate) && !(yield* fileExists(join(changesetDir, `${candidate}.md`)))) {
102
+ chosen.add(candidate);
103
+ return candidate;
104
+ }
130
105
  }
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;
106
+ let attempt = 0;
107
+ let fallback = `${pickRandomTriplet()}-${Date.now()}`;
108
+ while (chosen.has(fallback) || (yield* fileExists(join(changesetDir, `${fallback}.md`)))) fallback = `${pickRandomTriplet()}-${Date.now()}-${++attempt}`;
109
+ chosen.add(fallback);
110
+ return fallback;
111
+ });
137
112
  }
138
113
  /**
139
114
  * Strict detection of "pure dependency changesets" per the documented
@@ -184,43 +159,59 @@ function isPureDependencyChangeset(content) {
184
159
  package: pkg
185
160
  };
186
161
  }
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) {
162
+ /**
163
+ * List `.changeset/*.md` files (excluding `README.md`) in `changesetDir`.
164
+ * A missing directory is today's behavior for "no changesets yet" and
165
+ * resolves to an empty list; any OTHER failure (e.g. a directory that
166
+ * exists but cannot be read) is a loud {@link ChangesetIOError} — an
167
+ * unreadable `.changeset` dir must not silently masquerade as "no
168
+ * changesets".
169
+ */
170
+ const listChangesetFiles = (fs, changesetDir) => fs.readDirectory(changesetDir).pipe(Effect.map((names) => names.filter((f) => f.endsWith(".md") && f !== "README.md").map((f) => join(changesetDir, f))), Effect.catchAll((e) => e._tag === "SystemError" && e.reason === "NotFound" ? Effect.succeed([]) : Effect.fail(new ChangesetIOError({
171
+ path: changesetDir,
172
+ operation: "list",
173
+ reason: String(e)
174
+ }))));
175
+ /**
176
+ * Pure-dependency changesets found in `changesetDir`. An individual file
177
+ * that cannot be read (e.g. removed mid-scan, permission race) is skipped —
178
+ * today's semantics — while a failure to list the directory itself
179
+ * propagates as a loud {@link ChangesetIOError}.
180
+ */
181
+ const findPureDependencyChangesets = (fs, changesetDir) => Effect.gen(function* () {
182
+ const files = yield* listChangesetFiles(fs, changesetDir);
192
183
  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);
184
+ for (const file of files) {
185
+ const content = yield* fs.readFileString(file).pipe(Effect.option);
186
+ if (Option.isNone(content)) continue;
187
+ const detection = isPureDependencyChangeset(content.value);
201
188
  if (detection.isPure && detection.package) result.push({
202
189
  file,
203
190
  package: detection.package
204
191
  });
205
192
  }
206
193
  return result;
207
- }
208
- function findMixedDependencyChangesets(changesetDir) {
194
+ });
195
+ /**
196
+ * Mixed dependency changesets (have a `## Dependencies` heading but fail
197
+ * the strict pure-changeset test) found in `changesetDir`. Same
198
+ * skip-unreadable-file / loud-list-failure semantics as
199
+ * {@link findPureDependencyChangesets}.
200
+ */
201
+ const findMixedDependencyChangesets = (fs, changesetDir) => Effect.gen(function* () {
202
+ const files = yield* listChangesetFiles(fs, changesetDir);
209
203
  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);
204
+ for (const file of files) {
205
+ const content = yield* fs.readFileString(file).pipe(Effect.option);
206
+ if (Option.isNone(content)) continue;
207
+ if (/^## Dependencies\b/m.test(content.value) && !isPureDependencyChangeset(content.value).isPure) result.push(file);
218
208
  }
219
209
  return result;
220
- }
210
+ });
221
211
  /**
222
- * Render a single-package, patch-bump changeset for a diff whose rows have
223
- * already been resolved/filtered by {@link resolveDiffRows}.
212
+ * Render a single-package, patch-bump changeset for a diff whose rows are
213
+ * already resolved (per-side, inside {@link computeWorkspaceDependencyDiffs})
214
+ * and devDep-filtered.
224
215
  */
225
216
  function renderChangesetContent(diff) {
226
217
  return `${`---\n"${diff.package}": patch\n---`}\n\n## Dependencies\n\n${serializeDependencyTableToMarkdown([...diff.rows])}\n`;
@@ -253,9 +244,9 @@ var DepsRegen = class extends DepsRegenBase {};
253
244
  * implementations, keeping the public `plan`/`execute` signatures
254
245
  * requirement-free (`R = never`).
255
246
  */
256
- function makeShape(reader, inspector, discovery, resolver, detector) {
257
- const provideResolver = Layer.succeed(CatalogResolver, resolver);
247
+ function makeShape(pit, inspector, discovery, detector, config, fs) {
258
248
  const provideDetector = Layer.succeed(PublishabilityDetector, detector);
249
+ const fileExists = (p) => fs.exists(p).pipe(Effect.orElseSucceed(() => false));
259
250
  const plan = (options) => Effect.gen(function* () {
260
251
  const resolvedCwd = resolve(options.cwd);
261
252
  const changesetDir = join(resolvedCwd, ".changeset");
@@ -265,41 +256,58 @@ function makeShape(reader, inspector, discovery, resolver, detector) {
265
256
  if (!baseBranch) baseBranch = (yield* inspector.inspect(resolvedCwd).pipe(Effect.catchTag("ConfigurationError", () => Effect.succeed({ baseBranch: "main" })))).baseBranch;
266
257
  fromRef = yield* gitMergeBase(resolvedCwd, baseBranch);
267
258
  }
268
- const rawDiffs = computeWorkspaceDependencyDiffs(yield* reader.snapshotAt(resolvedCwd, fromRef), options.to ? yield* reader.snapshotAt(resolvedCwd, options.to) : snapshotFromWorktree(resolvedCwd));
259
+ const rawDiffs = computeWorkspaceDependencyDiffs(yield* pit.at(fromRef, { cwd: resolvedCwd }), options.to ? yield* pit.at(options.to, { cwd: resolvedCwd }) : yield* pit.worktree({ cwd: resolvedCwd }));
269
260
  const targetPkg = options.package;
270
- const publishable = yield* listPublishablePackageNames(yield* discovery.listPackages(resolvedCwd)).pipe(Effect.provide(provideDetector));
261
+ const livePackages = yield* discovery.listPackages(resolvedCwd);
262
+ const publishable = yield* listPublishablePackageNames(livePackages, resolvedCwd).pipe(Effect.provide(provideDetector));
263
+ const versionPrivate = yield* config.versionPrivate(resolvedCwd);
264
+ const inScope = /* @__PURE__ */ new Set();
265
+ for (const pkg of livePackages) {
266
+ if (yield* config.isIgnored(pkg.name, resolvedCwd)) continue;
267
+ if (publishable.has(pkg.name) || versionPrivate) inScope.add(pkg.name);
268
+ }
269
+ const targetIgnored = targetPkg ? yield* config.isIgnored(targetPkg, resolvedCwd) : false;
271
270
  const keepDevDeps = options.includeDevDeps === true;
272
- const scoped = targetPkg ? rawDiffs.filter((d) => d.package === targetPkg) : rawDiffs.filter((d) => publishable.has(d.package));
271
+ const scoped = targetPkg ? targetIgnored ? [] : rawDiffs.filter((d) => d.package === targetPkg) : rawDiffs.filter((d) => inScope.has(d.package));
273
272
  const resolved = [];
274
273
  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);
274
+ const rows = keepDevDeps ? [...diff.rows] : diff.rows.filter((r) => r.type !== "devDependency");
275
+ if (rows.length > 0) resolved.push({
276
+ ...diff,
277
+ rows: sortDependencyRows(rows)
278
+ });
277
279
  }
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));
280
+ const existingPure = yield* findPureDependencyChangesets(fs, changesetDir);
281
+ const skippedMixed = yield* findMixedDependencyChangesets(fs, changesetDir);
282
+ const toDelete = targetPkg ? targetIgnored ? [] : existingPure.filter((p) => p.package === targetPkg) : existingPure.filter((p) => inScope.has(p.package));
281
283
  const chosenFilenames = /* @__PURE__ */ new Set();
282
- return {
283
- toDelete,
284
- toWrite: resolved.map((diff) => ({
285
- file: join(changesetDir, `${randomFilename(changesetDir, chosenFilenames)}.md`),
284
+ const toWrite = [];
285
+ for (const diff of resolved) {
286
+ const filename = yield* randomFilename(fileExists, changesetDir, chosenFilenames);
287
+ toWrite.push({
288
+ file: join(changesetDir, `${filename}.md`),
286
289
  package: diff.package,
287
290
  diff
288
- })),
291
+ });
292
+ }
293
+ return {
294
+ toDelete,
295
+ toWrite,
289
296
  skippedMixed
290
297
  };
291
298
  });
292
- const execute = (plan) => Effect.sync(() => {
299
+ const execute = (plan) => Effect.gen(function* () {
293
300
  const deleted = [];
294
301
  const written = [];
295
302
  for (const entry of plan.toWrite) {
296
- writeFileSync(entry.file, renderChangesetContent(entry.diff));
303
+ yield* fs.writeFileString(entry.file, renderChangesetContent(entry.diff)).pipe(Effect.mapError((e) => new ChangesetIOError({
304
+ path: entry.file,
305
+ operation: "write",
306
+ reason: String(e)
307
+ })));
297
308
  written.push(entry.file);
298
309
  }
299
- for (const entry of plan.toDelete) try {
300
- unlinkSync(entry.file);
301
- deleted.push(entry.file);
302
- } catch {}
310
+ for (const entry of plan.toDelete) if (yield* Effect.isSuccess(fs.remove(entry.file))) deleted.push(entry.file);
303
311
  return {
304
312
  deleted,
305
313
  written,
@@ -314,15 +322,46 @@ function makeShape(reader, inspector, discovery, resolver, detector) {
314
322
  /**
315
323
  * Live layer for {@link DepsRegen}.
316
324
  *
317
- * Requires {@link WorkspaceSnapshotReader}, {@link ConfigInspector},
318
- * `WorkspaceDiscovery`, `CatalogResolver`, and `PublishabilityDetector`
319
- * (the last three from `workspaces-effect`).
325
+ * Requires `PointInTimeWorkspace`, `WorkspaceDiscovery`,
326
+ * `PublishabilityDetector` (all from `workspaces-effect`),
327
+ * {@link ConfigInspector}, {@link ChangesetConfig}, and
328
+ * `FileSystem.FileSystem` (resolved once at construction and closed over by
329
+ * the shape, keeping `plan`/`execute` themselves requirement-free).
320
330
  *
321
331
  * @public
322
332
  */
323
333
  const DepsRegenLive = Layer.effect(DepsRegen, Effect.gen(function* () {
324
- return makeShape(yield* WorkspaceSnapshotReader, yield* ConfigInspector, yield* WorkspaceDiscovery, yield* CatalogResolver, yield* PublishabilityDetector);
334
+ return makeShape(yield* PointInTimeWorkspace, yield* ConfigInspector, yield* WorkspaceDiscovery, yield* PublishabilityDetector, yield* ChangesetConfig, yield* FileSystem.FileSystem);
325
335
  }));
336
+ const WorkspaceGraph = Layer.mergeAll(WorkspaceRootLive, WorkspaceDiscoveryLive.pipe(Layer.provide(WorkspaceRootLive)));
337
+ const ConfigGraph = ChangesetConfigLive.pipe(Layer.provide(ChangesetConfigReaderLive));
338
+ /**
339
+ * Batteries-included {@link DepsRegen} layer: silk's opinionated default
340
+ * composition of the full dependency graph. Only the platform services
341
+ * remain — note that {@link PointInTimeWorkspace} reads git history, so
342
+ * this layer genuinely requires `CommandExecutor` in addition to
343
+ * `FileSystem`/`Path`: provide a git-capable platform layer
344
+ * (`NodeContext.layer`), not a bare filesystem-only layer.
345
+ *
346
+ * Gating uses silk's adaptive publishability detector
347
+ * ({@link PublishabilityDetectorAdaptiveLive}), so the default semantics
348
+ * are "versionable minus ignored" — identical to the savvy CLI and MCP
349
+ * runtimes. Consumers who need to swap any dependency (test detectors,
350
+ * alternate config sources) should keep composing {@link DepsRegenLive}
351
+ * directly; this layer is purely additive.
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * import { NodeContext } from "@effect/platform-node";
356
+ * import { Layer } from "effect";
357
+ * import { Changesets } from "@savvy-web/silk-effects";
358
+ *
359
+ * const depsRegen = Changesets.DepsRegenDefault.pipe(Layer.provide(NodeContext.layer));
360
+ * ```
361
+ *
362
+ * @public
363
+ */
364
+ const DepsRegenDefault = DepsRegenLive.pipe(Layer.provide(PointInTimeWorkspaceLive.pipe(Layer.provide(WorkspaceGraph))), Layer.provide(ConfigInspectorLive.pipe(Layer.provide(Layer.mergeAll(ChangesetConfigReaderLive, WorkspaceGraph)))), Layer.provide(PublishabilityDetectorAdaptiveLive.pipe(Layer.provide(ConfigGraph))), Layer.provide(ConfigGraph), Layer.provide(WorkspaceGraph));
326
365
 
327
366
  //#endregion
328
- export { DepsRegen, DepsRegenBase, DepsRegenLive, isPureDependencyChangeset, resolveDiffRows };
367
+ export { DepsRegen, DepsRegenBase, DepsRegenDefault, DepsRegenLive, isPureDependencyChangeset };