@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.
- package/changesets/errors.js +44 -1
- package/changesets/index.js +8 -11
- package/changesets/services/deps-regen.js +152 -113
- package/changesets/services/release-planner.js +105 -81
- package/changesets/utils/dep-diff.js +81 -47
- package/changesets/utils/git.js +51 -0
- package/changesets/utils/publishability.js +14 -2
- package/index.d.ts +157 -141
- package/package.json +2 -2
- package/changesets/services/workspace-snapshot.js +0 -181
- package/changesets/utils/worktree-snapshot.js +0 -142
package/changesets/errors.js
CHANGED
|
@@ -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 };
|
package/changesets/index.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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,
|
|
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 {
|
|
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 {
|
|
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)
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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
|
|
114
|
-
* moments earlier in the same call — the `chosen` set closes
|
|
115
|
-
* The triplet space is 1,000 combinations, so a busy repo can
|
|
116
|
-
* exhaust it across runs; fall back to a timestamp suffix after
|
|
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
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
chosen.
|
|
129
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
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
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
223
|
-
* already
|
|
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(
|
|
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*
|
|
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
|
|
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) =>
|
|
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
|
|
276
|
-
if (
|
|
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) =>
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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.
|
|
299
|
+
const execute = (plan) => Effect.gen(function* () {
|
|
293
300
|
const deleted = [];
|
|
294
301
|
const written = [];
|
|
295
302
|
for (const entry of plan.toWrite) {
|
|
296
|
-
|
|
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)
|
|
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
|
|
318
|
-
* `
|
|
319
|
-
*
|
|
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*
|
|
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
|
|
367
|
+
export { DepsRegen, DepsRegenBase, DepsRegenDefault, DepsRegenLive, isPureDependencyChangeset };
|