@savvy-web/silk 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -49,9 +49,11 @@ const require_presets = require('./remark/presets.cjs');
49
49
  //#region ../silk-effects/dist/dev/pkg/changesets/index.js
50
50
  var changesets_exports = /* @__PURE__ */ require_runtime.__exportAll({
51
51
  AggregateDependencyTablesPlugin: () => require_aggregate_dependency_tables.AggregateDependencyTablesPlugin,
52
+ BranchAnalysisSchema: () => require_branch_analyzer.BranchAnalysisSchema,
52
53
  BranchAnalyzer: () => require_branch_analyzer.BranchAnalyzer,
53
54
  BranchAnalyzerBase: () => require_branch_analyzer.BranchAnalyzerBase,
54
55
  BranchAnalyzerLive: () => require_branch_analyzer.BranchAnalyzerLive,
56
+ BranchFileEntrySchema: () => require_branch_analyzer.BranchFileEntrySchema,
55
57
  Categories: () => require_categories.Categories,
56
58
  Changelog: () => require_changelog.Changelog,
57
59
  ChangelogService: () => require_changelog$1.ChangelogService,
@@ -63,6 +65,8 @@ var changesets_exports = /* @__PURE__ */ require_runtime.__exportAll({
63
65
  ChangesetSummarySchema: () => require_changeset.ChangesetSummarySchema,
64
66
  ChangesetValidationError: () => require_errors.ChangesetValidationError,
65
67
  ChangesetValidationErrorBase: () => require_errors.ChangesetValidationErrorBase,
68
+ ClassificationReasonSchema: () => require_config_inspector.ClassificationReasonSchema,
69
+ ClassificationSchema: () => require_config_inspector.ClassificationSchema,
66
70
  CommitHashSchema: () => require_git.CommitHashSchema,
67
71
  ConfigInspector: () => require_config_inspector.ConfigInspector,
68
72
  ConfigInspectorBase: () => require_config_inspector.ConfigInspectorBase,
@@ -80,6 +84,7 @@ var changesets_exports = /* @__PURE__ */ require_runtime.__exportAll({
80
84
  DependencyTableTypeSchema: () => require_dependency_table.DependencyTableTypeSchema,
81
85
  DependencyTypeSchema: () => require_changeset.DependencyTypeSchema,
82
86
  DependencyUpdateSchema: () => require_changeset.DependencyUpdateSchema,
87
+ FileStatusSchema: () => require_branch_analyzer.FileStatusSchema,
83
88
  GitError: () => require_errors.GitError,
84
89
  GitErrorBase: () => require_errors.GitErrorBase,
85
90
  GitHubApiError: () => require_errors.GitHubApiError,
@@ -90,6 +95,7 @@ var changesets_exports = /* @__PURE__ */ require_runtime.__exportAll({
90
95
  GitHubServiceBase: () => require_github.GitHubServiceBase,
91
96
  GlobSchema: () => require_package_scope.GlobSchema,
92
97
  HeadingHierarchyRule: () => require_heading_hierarchy.HeadingHierarchyRule,
98
+ InspectedConfigSchema: () => require_config_inspector.InspectedConfigSchema,
93
99
  IssueLinkRefsPlugin: () => require_issue_link_refs.IssueLinkRefsPlugin,
94
100
  IssueNumberSchema: () => require_github$1.IssueNumberSchema,
95
101
  JsonPathSchema: () => require_version_files.JsonPathSchema,
@@ -114,6 +120,8 @@ var changesets_exports = /* @__PURE__ */ require_runtime.__exportAll({
114
120
  ReorderSectionsPlugin: () => require_reorder_sections.ReorderSectionsPlugin,
115
121
  RepoSchema: () => require_options.RepoSchema,
116
122
  RequiredSectionsRule: () => require_required_sections.RequiredSectionsRule,
123
+ ResolvedPackageScopeSchema: () => require_config_inspector.ResolvedPackageScopeSchema,
124
+ ResolvedVersionFileSchema: () => require_config_inspector.ResolvedVersionFileSchema,
117
125
  SectionCategorySchema: () => require_types.SectionCategorySchema,
118
126
  SilkChangesetPreset: () => require_presets.SilkChangesetPreset,
119
127
  SilkChangesetTransformPreset: () => require_presets.SilkChangesetTransformPreset,
@@ -25,8 +25,8 @@ import { MergeSectionsPlugin } from "./remark/plugins/merge-sections.js";
25
25
  import { NormalizeFormatPlugin } from "./remark/plugins/normalize-format.js";
26
26
  import { ReorderSectionsPlugin } from "./remark/plugins/reorder-sections.js";
27
27
  import { ChangelogTransformer } from "./api/transformer.js";
28
- import { ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, makeConfigInspectorTest } from "./services/config-inspector.js";
29
- import { BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, makeBranchAnalyzerTest } from "./services/branch-analyzer.js";
28
+ import { ClassificationReasonSchema, ClassificationSchema, ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, InspectedConfigSchema, ResolvedPackageScopeSchema, ResolvedVersionFileSchema, makeConfigInspectorTest } from "./services/config-inspector.js";
29
+ import { BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchFileEntrySchema, FileStatusSchema, makeBranchAnalyzerTest } from "./services/branch-analyzer.js";
30
30
  import { ChangelogService, ChangelogServiceBase } from "./services/changelog.js";
31
31
  import { WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive } from "./services/workspace-snapshot.js";
32
32
  import { SectionCategorySchema } from "./categories/types.js";
@@ -49,9 +49,11 @@ import { SilkChangesetPreset, SilkChangesetTransformPreset } from "./remark/pres
49
49
  //#region ../silk-effects/dist/dev/pkg/changesets/index.js
50
50
  var changesets_exports = /* @__PURE__ */ __exportAll({
51
51
  AggregateDependencyTablesPlugin: () => AggregateDependencyTablesPlugin,
52
+ BranchAnalysisSchema: () => BranchAnalysisSchema,
52
53
  BranchAnalyzer: () => BranchAnalyzer,
53
54
  BranchAnalyzerBase: () => BranchAnalyzerBase,
54
55
  BranchAnalyzerLive: () => BranchAnalyzerLive,
56
+ BranchFileEntrySchema: () => BranchFileEntrySchema,
55
57
  Categories: () => Categories,
56
58
  Changelog: () => Changelog,
57
59
  ChangelogService: () => ChangelogService,
@@ -63,6 +65,8 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
63
65
  ChangesetSummarySchema: () => ChangesetSummarySchema,
64
66
  ChangesetValidationError: () => ChangesetValidationError,
65
67
  ChangesetValidationErrorBase: () => ChangesetValidationErrorBase,
68
+ ClassificationReasonSchema: () => ClassificationReasonSchema,
69
+ ClassificationSchema: () => ClassificationSchema,
66
70
  CommitHashSchema: () => CommitHashSchema,
67
71
  ConfigInspector: () => ConfigInspector,
68
72
  ConfigInspectorBase: () => ConfigInspectorBase,
@@ -80,6 +84,7 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
80
84
  DependencyTableTypeSchema: () => DependencyTableTypeSchema,
81
85
  DependencyTypeSchema: () => DependencyTypeSchema,
82
86
  DependencyUpdateSchema: () => DependencyUpdateSchema,
87
+ FileStatusSchema: () => FileStatusSchema,
83
88
  GitError: () => GitError,
84
89
  GitErrorBase: () => GitErrorBase,
85
90
  GitHubApiError: () => GitHubApiError,
@@ -90,6 +95,7 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
90
95
  GitHubServiceBase: () => GitHubServiceBase,
91
96
  GlobSchema: () => GlobSchema,
92
97
  HeadingHierarchyRule: () => HeadingHierarchyRule,
98
+ InspectedConfigSchema: () => InspectedConfigSchema,
93
99
  IssueLinkRefsPlugin: () => IssueLinkRefsPlugin,
94
100
  IssueNumberSchema: () => IssueNumberSchema,
95
101
  JsonPathSchema: () => JsonPathSchema,
@@ -114,6 +120,8 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
114
120
  ReorderSectionsPlugin: () => ReorderSectionsPlugin,
115
121
  RepoSchema: () => RepoSchema,
116
122
  RequiredSectionsRule: () => RequiredSectionsRule,
123
+ ResolvedPackageScopeSchema: () => ResolvedPackageScopeSchema,
124
+ ResolvedVersionFileSchema: () => ResolvedVersionFileSchema,
117
125
  SectionCategorySchema: () => SectionCategorySchema,
118
126
  SilkChangesetPreset: () => SilkChangesetPreset,
119
127
  SilkChangesetTransformPreset: () => SilkChangesetTransformPreset,
@@ -5,6 +5,64 @@ let node_child_process = require("node:child_process");
5
5
 
6
6
  //#region ../silk-effects/dist/dev/pkg/changesets/services/branch-analyzer.js
7
7
  /**
8
+ * `BranchAnalyzer` service — combine a git diff against the base branch with
9
+ * the per-file classification produced by {@link ConfigInspector}.
10
+ *
11
+ * @remarks
12
+ * This is the single call that the changeset-manager agent uses during its
13
+ * inventory step: one invocation returns the diff, the per-file package
14
+ * attribution, the set of packages affected by the branch, and the list of
15
+ * paths that did not map to any known release surface (so the agent can ask
16
+ * the user about them rather than silently excluding).
17
+ *
18
+ * Base-branch resolution order (highest priority first):
19
+ *
20
+ * 1. Explicit `opts.baseBranch` passed to {@link BranchAnalyzerShape.analyzeBranch}.
21
+ * 2. `baseBranch` from `.changeset/config.json` (surfaced via the inspector).
22
+ * 3. The branch `origin/HEAD` points to (`git symbolic-ref refs/remotes/origin/HEAD`).
23
+ * 4. `"main"` as a final fallback.
24
+ *
25
+ * Diff resolution covers everything the user might commit before merging:
26
+ *
27
+ * 1. `git merge-base <base> HEAD` finds the common ancestor.
28
+ * 2. `git diff --name-status <merge-base>` (working tree vs merge-base)
29
+ * returns every committed, staged, AND unstaged change since the
30
+ * branch diverged. The two-arg `<merge-base>...HEAD` form would miss
31
+ * work-in-progress — what the user has open in their editor right
32
+ * now is exactly the state the agent needs to document.
33
+ * 3. `git ls-files --others --exclude-standard` adds untracked files
34
+ * (entirely new files not yet `git add`-ed). Each is reported with
35
+ * status `"added"`.
36
+ *
37
+ * The two results are deduped by path before classification. Renames
38
+ * are reported as `"renamed"` with the new path; the old path is
39
+ * discarded since the classifier needs a single canonical path per
40
+ * file.
41
+ *
42
+ * @see {@link BranchAnalyzer} for the service tag
43
+ * @see {@link BranchAnalyzerLive} for the production layer
44
+ * @see {@link ConfigInspector} for the underlying classification service
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+ /** Git diff status as reported by `--name-status`. @public */
49
+ const FileStatusSchema = effect.Schema.Literal("added", "modified", "deleted", "renamed", "copied", "typechange", "unmerged", "unknown").annotations({ identifier: "FileStatus" });
50
+ /** One file entry in the branch analysis output. @public */
51
+ const BranchFileEntrySchema = effect.Schema.Struct({
52
+ path: effect.Schema.String.annotations({ description: "Repo-relative path (the new path in the case of renames)." }),
53
+ status: FileStatusSchema,
54
+ package: effect.Schema.NullOr(effect.Schema.String).annotations({ description: "Owning package, or null if outside every known release surface." }),
55
+ reason: require_config_inspector.ClassificationReasonSchema
56
+ }).annotations({ identifier: "BranchFileEntry" });
57
+ /** Structured result of analyzing the current branch against its base. @public */
58
+ const BranchAnalysisSchema = effect.Schema.Struct({
59
+ baseBranch: effect.Schema.String,
60
+ mergeBaseSha: effect.Schema.String,
61
+ files: effect.Schema.Array(BranchFileEntrySchema),
62
+ packagesAffected: effect.Schema.Array(effect.Schema.String),
63
+ unmappedFiles: effect.Schema.Array(effect.Schema.String).annotations({ description: "Repo-relative paths whose package is null — candidates for an AskUserQuestion." })
64
+ }).annotations({ identifier: "BranchAnalysis" });
65
+ /**
8
66
  * Base class for {@link BranchAnalyzer}.
9
67
  *
10
68
  * @privateRemarks
@@ -233,7 +291,10 @@ function makeBranchAnalyzerTest(fixed) {
233
291
  }
234
292
 
235
293
  //#endregion
294
+ exports.BranchAnalysisSchema = BranchAnalysisSchema;
236
295
  exports.BranchAnalyzer = BranchAnalyzer;
237
296
  exports.BranchAnalyzerBase = BranchAnalyzerBase;
238
297
  exports.BranchAnalyzerLive = BranchAnalyzerLive;
298
+ exports.BranchFileEntrySchema = BranchFileEntrySchema;
299
+ exports.FileStatusSchema = FileStatusSchema;
239
300
  exports.makeBranchAnalyzerTest = makeBranchAnalyzerTest;
@@ -1,10 +1,68 @@
1
1
  import { GitError } from "../errors.js";
2
- import { ConfigInspector } from "./config-inspector.js";
3
- import { Context, Effect, Layer } from "effect";
2
+ import { ClassificationReasonSchema, ConfigInspector } from "./config-inspector.js";
3
+ import { Context, Effect, Layer, Schema } from "effect";
4
4
  import { execFileSync } from "node:child_process";
5
5
 
6
6
  //#region ../silk-effects/dist/dev/pkg/changesets/services/branch-analyzer.js
7
7
  /**
8
+ * `BranchAnalyzer` service — combine a git diff against the base branch with
9
+ * the per-file classification produced by {@link ConfigInspector}.
10
+ *
11
+ * @remarks
12
+ * This is the single call that the changeset-manager agent uses during its
13
+ * inventory step: one invocation returns the diff, the per-file package
14
+ * attribution, the set of packages affected by the branch, and the list of
15
+ * paths that did not map to any known release surface (so the agent can ask
16
+ * the user about them rather than silently excluding).
17
+ *
18
+ * Base-branch resolution order (highest priority first):
19
+ *
20
+ * 1. Explicit `opts.baseBranch` passed to {@link BranchAnalyzerShape.analyzeBranch}.
21
+ * 2. `baseBranch` from `.changeset/config.json` (surfaced via the inspector).
22
+ * 3. The branch `origin/HEAD` points to (`git symbolic-ref refs/remotes/origin/HEAD`).
23
+ * 4. `"main"` as a final fallback.
24
+ *
25
+ * Diff resolution covers everything the user might commit before merging:
26
+ *
27
+ * 1. `git merge-base <base> HEAD` finds the common ancestor.
28
+ * 2. `git diff --name-status <merge-base>` (working tree vs merge-base)
29
+ * returns every committed, staged, AND unstaged change since the
30
+ * branch diverged. The two-arg `<merge-base>...HEAD` form would miss
31
+ * work-in-progress — what the user has open in their editor right
32
+ * now is exactly the state the agent needs to document.
33
+ * 3. `git ls-files --others --exclude-standard` adds untracked files
34
+ * (entirely new files not yet `git add`-ed). Each is reported with
35
+ * status `"added"`.
36
+ *
37
+ * The two results are deduped by path before classification. Renames
38
+ * are reported as `"renamed"` with the new path; the old path is
39
+ * discarded since the classifier needs a single canonical path per
40
+ * file.
41
+ *
42
+ * @see {@link BranchAnalyzer} for the service tag
43
+ * @see {@link BranchAnalyzerLive} for the production layer
44
+ * @see {@link ConfigInspector} for the underlying classification service
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+ /** Git diff status as reported by `--name-status`. @public */
49
+ const FileStatusSchema = Schema.Literal("added", "modified", "deleted", "renamed", "copied", "typechange", "unmerged", "unknown").annotations({ identifier: "FileStatus" });
50
+ /** One file entry in the branch analysis output. @public */
51
+ const BranchFileEntrySchema = Schema.Struct({
52
+ path: Schema.String.annotations({ description: "Repo-relative path (the new path in the case of renames)." }),
53
+ status: FileStatusSchema,
54
+ package: Schema.NullOr(Schema.String).annotations({ description: "Owning package, or null if outside every known release surface." }),
55
+ reason: ClassificationReasonSchema
56
+ }).annotations({ identifier: "BranchFileEntry" });
57
+ /** Structured result of analyzing the current branch against its base. @public */
58
+ const BranchAnalysisSchema = Schema.Struct({
59
+ baseBranch: Schema.String,
60
+ mergeBaseSha: Schema.String,
61
+ files: Schema.Array(BranchFileEntrySchema),
62
+ packagesAffected: Schema.Array(Schema.String),
63
+ unmappedFiles: Schema.Array(Schema.String).annotations({ description: "Repo-relative paths whose package is null — candidates for an AskUserQuestion." })
64
+ }).annotations({ identifier: "BranchAnalysis" });
65
+ /**
8
66
  * Base class for {@link BranchAnalyzer}.
9
67
  *
10
68
  * @privateRemarks
@@ -233,4 +291,4 @@ function makeBranchAnalyzerTest(fixed) {
233
291
  }
234
292
 
235
293
  //#endregion
236
- export { BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, makeBranchAnalyzerTest };
294
+ export { BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, BranchFileEntrySchema, FileStatusSchema, makeBranchAnalyzerTest };
@@ -1,13 +1,89 @@
1
1
  const require_errors = require('../errors.cjs');
2
2
  const require_options = require('../schemas/options.cjs');
3
3
  const require_ChangesetConfigReader = require('../../services/ChangesetConfigReader.cjs');
4
- const require_index = require('../../../../../../../node_modules/.pnpm/tinyglobby@0.2.17/node_modules/tinyglobby/dist/index.cjs');
5
- const require_index$1 = require('../../../../../../../node_modules/.pnpm/workspaces-effect@1.2.0_@effect_platform@0.96.1_effect@3.21.3__effect@3.21.3/node_modules/workspaces-effect/index.cjs');
4
+ const require_index = require('../../../../../../../node_modules/.pnpm/workspaces-effect@1.2.0_@effect_platform@0.96.1_effect@3.21.3__effect@3.21.3/node_modules/workspaces-effect/index.cjs');
5
+ const require_SilkPublishability = require('../../services/SilkPublishability.cjs');
6
+ const require_index$1 = require('../../../../../../../node_modules/.pnpm/tinyglobby@0.2.17/node_modules/tinyglobby/dist/index.cjs');
6
7
  let effect = require("effect");
7
8
  let node_path = require("node:path");
9
+ let _effect_platform = require("@effect/platform");
8
10
 
9
11
  //#region ../silk-effects/dist/dev/pkg/changesets/services/config-inspector.js
10
12
  /**
13
+ * `ConfigInspector` service — surface the project's `.changeset/config.json`
14
+ * in a structured, validated form that agents and tooling can consume.
15
+ *
16
+ * @remarks
17
+ * The inspector handles three responsibilities that the schemas alone cannot:
18
+ *
19
+ * 1. **Legacy normalization.** Configs from 0.8.x still use the flat
20
+ * top-level `versionFiles[]` array. When that field is populated, the
21
+ * inspector emits a one-line deprecation warning to stderr (naming the
22
+ * config path and the required edit) and folds each legacy entry into
23
+ * the equivalent `packages[entry.package].versionFiles` shape for
24
+ * downstream consumers. Removed in 1.0.0.
25
+ * 2. **Resolution against the workspace.** Each `packages` key must resolve
26
+ * to a known workspace package via `WorkspaceDiscovery`. Unknown keys
27
+ * surface as a {@link ConfigurationError}. The inspector returns the
28
+ * package's absolute workspace directory alongside the resolved scope.
29
+ * 3. **Overlap detection.** Cross-package validation rules that the schemas
30
+ * cannot enforce:
31
+ * - `additionalScopes` of two different packages must not overlap.
32
+ * - `additionalScopes` of one package must not shadow another package's
33
+ * workspace directory.
34
+ * - Two `versionFiles` entries (within or across packages) must not
35
+ * resolve to the same `(file, $.path)` tuple.
36
+ *
37
+ * Validation is performed eagerly on the first {@link ConfigInspectorShape.inspect}
38
+ * call. The resolved state is cached internally per `cwd` so subsequent
39
+ * {@link ConfigInspectorShape.classify} calls reuse it.
40
+ *
41
+ * @see {@link ConfigInspector} for the Effect service tag
42
+ * @see {@link ConfigInspectorLive} for the production layer
43
+ *
44
+ * @packageDocumentation
45
+ */
46
+ /** A `versionFiles` entry expanded to its absolute target paths. @public */
47
+ const ResolvedVersionFileSchema = effect.Schema.Struct({
48
+ glob: effect.Schema.String,
49
+ paths: effect.Schema.Array(effect.Schema.String).annotations({ description: "JSONPath expressions to update (defaults to [\"$.version\"])." }),
50
+ matchedFiles: effect.Schema.Array(effect.Schema.String)
51
+ }).annotations({ identifier: "ResolvedVersionFile" });
52
+ /** A package's resolved release surface. @public */
53
+ const ResolvedPackageScopeSchema = effect.Schema.Struct({
54
+ name: effect.Schema.String,
55
+ workspaceDir: effect.Schema.String,
56
+ version: effect.Schema.String,
57
+ additionalScopes: effect.Schema.Array(effect.Schema.String),
58
+ additionalScopeFiles: effect.Schema.Array(effect.Schema.String),
59
+ versionFiles: effect.Schema.Array(ResolvedVersionFileSchema)
60
+ }).annotations({ identifier: "ResolvedPackageScope" });
61
+ /** Structured representation of a resolved `.changeset/config.json`. @public */
62
+ const InspectedConfigSchema = effect.Schema.Struct({
63
+ configPath: effect.Schema.String,
64
+ projectDir: effect.Schema.String.annotations({ description: "Absolute project root (the directory containing .changeset/)." }),
65
+ changelog: effect.Schema.NullOr(effect.Schema.String),
66
+ baseBranch: effect.Schema.String,
67
+ access: effect.Schema.Literal("public", "restricted"),
68
+ ignore: effect.Schema.Array(effect.Schema.String),
69
+ packages: effect.Schema.Array(ResolvedPackageScopeSchema),
70
+ legacyVersionFilesUsed: effect.Schema.Boolean
71
+ }).annotations({ identifier: "InspectedConfig" });
72
+ /** Reason a path was attributed to a package (or left unmapped). @public */
73
+ const ClassificationReasonSchema = effect.Schema.Union(effect.Schema.Literal("workspace"), effect.Schema.Struct({
74
+ kind: effect.Schema.Literal("additionalScope"),
75
+ glob: effect.Schema.String
76
+ }), effect.Schema.Struct({
77
+ kind: effect.Schema.Literal("versionFile"),
78
+ glob: effect.Schema.String
79
+ }), effect.Schema.Null).annotations({ identifier: "ClassificationReason" });
80
+ /** The result of classifying a single path against a resolved config. @public */
81
+ const ClassificationSchema = effect.Schema.Struct({
82
+ path: effect.Schema.String,
83
+ package: effect.Schema.NullOr(effect.Schema.String),
84
+ reason: ClassificationReasonSchema
85
+ }).annotations({ identifier: "Classification" });
86
+ /**
11
87
  * Base class for {@link ConfigInspector}.
12
88
  *
13
89
  * @privateRemarks
@@ -104,7 +180,7 @@ function normalizeLegacyOptions(options, configPath) {
104
180
  * repo-relative strings. Honors negation patterns and ignores `node_modules`.
105
181
  */
106
182
  function materializeGlob(glob, cwd) {
107
- return require_index.globSync(glob, {
183
+ return require_index$1.globSync(glob, {
108
184
  cwd,
109
185
  ignore: ["**/node_modules/**"],
110
186
  dot: true
@@ -152,6 +228,48 @@ function buildResolvedScopes(params) {
152
228
  return scopes;
153
229
  }
154
230
  /**
231
+ * Read a workspace package's raw package.json. A genuinely missing manifest
232
+ * resolves to `null` so the caller can skip that package — a workspace
233
+ * directory without a `package.json` is not a release surface. Any OTHER
234
+ * failure (unreadable file, malformed JSON) is surfaced as a
235
+ * {@link ConfigurationError} carrying the package path, rather than silently
236
+ * dropping the package, which would cascade into wrong attribution.
237
+ */
238
+ function readRawPackageJson(fs, pkgDir) {
239
+ const pkgJsonPath = (0, node_path.join)(pkgDir, "package.json");
240
+ return fs.readFileString(pkgJsonPath).pipe(effect.Effect.flatMap((content) => effect.Effect.try(() => JSON.parse(content))), effect.Effect.catchAll((err) => {
241
+ if (err.reason === "NotFound") return effect.Effect.succeed(null);
242
+ return effect.Effect.fail(new require_errors.ConfigurationError({
243
+ field: "workspace",
244
+ reason: `Failed to read or parse ${pkgJsonPath}: ${err instanceof Error ? err.message : String(err)}`
245
+ }));
246
+ }));
247
+ }
248
+ /**
249
+ * Build resolved scopes from the discovered workspace packages, keeping only
250
+ * those that are a release surface (declare publishConfig that yields at least
251
+ * one publish target). Used when `.changeset/config.json` declares no explicit
252
+ * `packages` record. The changeset `ignore` list is intentionally NOT consulted
253
+ * here — an ignored package still declares publishConfig and remains a valid
254
+ * changeset target.
255
+ */
256
+ function buildFallbackScopes(fs, workspaces) {
257
+ return effect.Effect.forEach(workspaces, (ws) => effect.Effect.gen(function* () {
258
+ const raw = yield* readRawPackageJson(fs, ws.path);
259
+ if (raw === null) return [];
260
+ const binding = yield* require_SilkPublishability.readTargetsBinding(fs, ws.path);
261
+ if (require_SilkPublishability.SilkPublishability.detect(ws.name, raw, binding).length === 0) return [];
262
+ return [{
263
+ name: ws.name,
264
+ workspaceDir: ws.path,
265
+ version: ws.version,
266
+ additionalScopes: [],
267
+ additionalScopeFiles: [],
268
+ versionFiles: []
269
+ }];
270
+ })).pipe(effect.Effect.map((nested) => nested.flat()));
271
+ }
272
+ /**
155
273
  * Cross-package validation: no overlap in `additionalScopes`, no shadowing
156
274
  * of another workspace package's directory (regardless of whether that
157
275
  * package is itself declared in `config.packages`), and no duplicate
@@ -209,7 +327,7 @@ function configErrorFromParseError(parseError, configPath) {
209
327
  * Each shape carries a private cache keyed by absolute project dir so
210
328
  * repeat `inspect`/`classify` calls reuse the materialized state.
211
329
  */
212
- function makeShape(reader, discovery) {
330
+ function makeShape(reader, discovery, fs) {
213
331
  const cache = /* @__PURE__ */ new Map();
214
332
  const inspect = (cwd) => effect.Effect.gen(function* () {
215
333
  const projectDir = (0, node_path.resolve)(cwd);
@@ -245,8 +363,9 @@ function makeShape(reader, discovery) {
245
363
  path: w.path,
246
364
  version: w.version
247
365
  }));
366
+ const hasExplicitPackages = Object.keys(decodedOptions.packages ?? {}).length > 0;
248
367
  let scopes;
249
- try {
368
+ if (hasExplicitPackages) try {
250
369
  scopes = buildResolvedScopes({
251
370
  options: decodedOptions,
252
371
  workspaces,
@@ -258,6 +377,7 @@ function makeShape(reader, discovery) {
258
377
  if (e instanceof require_errors.ConfigurationError) return yield* effect.Effect.fail(e);
259
378
  throw e;
260
379
  }
380
+ else scopes = yield* buildFallbackScopes(fs, workspaces);
261
381
  const inspected = {
262
382
  configPath,
263
383
  projectDir,
@@ -332,7 +452,7 @@ function classifyOne(inspected, path) {
332
452
  * @public
333
453
  */
334
454
  const ConfigInspectorLive = effect.Layer.effect(ConfigInspector, effect.Effect.gen(function* () {
335
- return makeShape(yield* require_ChangesetConfigReader.ChangesetConfigReader, yield* require_index$1.WorkspaceDiscovery);
455
+ return makeShape(yield* require_ChangesetConfigReader.ChangesetConfigReader, yield* require_index.WorkspaceDiscovery, yield* _effect_platform.FileSystem.FileSystem);
336
456
  }));
337
457
  /**
338
458
  * Test factory — build a {@link ConfigInspector} that returns a fixed
@@ -352,7 +472,12 @@ function makeConfigInspectorTest(fixed) {
352
472
  }
353
473
 
354
474
  //#endregion
475
+ exports.ClassificationReasonSchema = ClassificationReasonSchema;
476
+ exports.ClassificationSchema = ClassificationSchema;
355
477
  exports.ConfigInspector = ConfigInspector;
356
478
  exports.ConfigInspectorBase = ConfigInspectorBase;
357
479
  exports.ConfigInspectorLive = ConfigInspectorLive;
480
+ exports.InspectedConfigSchema = InspectedConfigSchema;
481
+ exports.ResolvedPackageScopeSchema = ResolvedPackageScopeSchema;
482
+ exports.ResolvedVersionFileSchema = ResolvedVersionFileSchema;
358
483
  exports.makeConfigInspectorTest = makeConfigInspectorTest;
@@ -1,13 +1,89 @@
1
1
  import { ConfigurationError } from "../errors.js";
2
2
  import { ChangesetOptionsSchema } from "../schemas/options.js";
3
3
  import { ChangesetConfigReader } from "../../services/ChangesetConfigReader.js";
4
- import { globSync } from "../../../../../../../node_modules/.pnpm/tinyglobby@0.2.17/node_modules/tinyglobby/dist/index.js";
5
4
  import { WorkspaceDiscovery } from "../../../../../../../node_modules/.pnpm/workspaces-effect@1.2.0_@effect_platform@0.96.1_effect@3.21.3__effect@3.21.3/node_modules/workspaces-effect/index.js";
5
+ import { SilkPublishability, readTargetsBinding } from "../../services/SilkPublishability.js";
6
+ import { globSync } from "../../../../../../../node_modules/.pnpm/tinyglobby@0.2.17/node_modules/tinyglobby/dist/index.js";
6
7
  import { Context, Effect, Layer, Schema } from "effect";
7
8
  import { isAbsolute, join, relative, resolve } from "node:path";
9
+ import { FileSystem } from "@effect/platform";
8
10
 
9
11
  //#region ../silk-effects/dist/dev/pkg/changesets/services/config-inspector.js
10
12
  /**
13
+ * `ConfigInspector` service — surface the project's `.changeset/config.json`
14
+ * in a structured, validated form that agents and tooling can consume.
15
+ *
16
+ * @remarks
17
+ * The inspector handles three responsibilities that the schemas alone cannot:
18
+ *
19
+ * 1. **Legacy normalization.** Configs from 0.8.x still use the flat
20
+ * top-level `versionFiles[]` array. When that field is populated, the
21
+ * inspector emits a one-line deprecation warning to stderr (naming the
22
+ * config path and the required edit) and folds each legacy entry into
23
+ * the equivalent `packages[entry.package].versionFiles` shape for
24
+ * downstream consumers. Removed in 1.0.0.
25
+ * 2. **Resolution against the workspace.** Each `packages` key must resolve
26
+ * to a known workspace package via `WorkspaceDiscovery`. Unknown keys
27
+ * surface as a {@link ConfigurationError}. The inspector returns the
28
+ * package's absolute workspace directory alongside the resolved scope.
29
+ * 3. **Overlap detection.** Cross-package validation rules that the schemas
30
+ * cannot enforce:
31
+ * - `additionalScopes` of two different packages must not overlap.
32
+ * - `additionalScopes` of one package must not shadow another package's
33
+ * workspace directory.
34
+ * - Two `versionFiles` entries (within or across packages) must not
35
+ * resolve to the same `(file, $.path)` tuple.
36
+ *
37
+ * Validation is performed eagerly on the first {@link ConfigInspectorShape.inspect}
38
+ * call. The resolved state is cached internally per `cwd` so subsequent
39
+ * {@link ConfigInspectorShape.classify} calls reuse it.
40
+ *
41
+ * @see {@link ConfigInspector} for the Effect service tag
42
+ * @see {@link ConfigInspectorLive} for the production layer
43
+ *
44
+ * @packageDocumentation
45
+ */
46
+ /** A `versionFiles` entry expanded to its absolute target paths. @public */
47
+ const ResolvedVersionFileSchema = Schema.Struct({
48
+ glob: Schema.String,
49
+ paths: Schema.Array(Schema.String).annotations({ description: "JSONPath expressions to update (defaults to [\"$.version\"])." }),
50
+ matchedFiles: Schema.Array(Schema.String)
51
+ }).annotations({ identifier: "ResolvedVersionFile" });
52
+ /** A package's resolved release surface. @public */
53
+ const ResolvedPackageScopeSchema = Schema.Struct({
54
+ name: Schema.String,
55
+ workspaceDir: Schema.String,
56
+ version: Schema.String,
57
+ additionalScopes: Schema.Array(Schema.String),
58
+ additionalScopeFiles: Schema.Array(Schema.String),
59
+ versionFiles: Schema.Array(ResolvedVersionFileSchema)
60
+ }).annotations({ identifier: "ResolvedPackageScope" });
61
+ /** Structured representation of a resolved `.changeset/config.json`. @public */
62
+ const InspectedConfigSchema = Schema.Struct({
63
+ configPath: Schema.String,
64
+ projectDir: Schema.String.annotations({ description: "Absolute project root (the directory containing .changeset/)." }),
65
+ changelog: Schema.NullOr(Schema.String),
66
+ baseBranch: Schema.String,
67
+ access: Schema.Literal("public", "restricted"),
68
+ ignore: Schema.Array(Schema.String),
69
+ packages: Schema.Array(ResolvedPackageScopeSchema),
70
+ legacyVersionFilesUsed: Schema.Boolean
71
+ }).annotations({ identifier: "InspectedConfig" });
72
+ /** Reason a path was attributed to a package (or left unmapped). @public */
73
+ const ClassificationReasonSchema = Schema.Union(Schema.Literal("workspace"), Schema.Struct({
74
+ kind: Schema.Literal("additionalScope"),
75
+ glob: Schema.String
76
+ }), Schema.Struct({
77
+ kind: Schema.Literal("versionFile"),
78
+ glob: Schema.String
79
+ }), Schema.Null).annotations({ identifier: "ClassificationReason" });
80
+ /** The result of classifying a single path against a resolved config. @public */
81
+ const ClassificationSchema = Schema.Struct({
82
+ path: Schema.String,
83
+ package: Schema.NullOr(Schema.String),
84
+ reason: ClassificationReasonSchema
85
+ }).annotations({ identifier: "Classification" });
86
+ /**
11
87
  * Base class for {@link ConfigInspector}.
12
88
  *
13
89
  * @privateRemarks
@@ -152,6 +228,48 @@ function buildResolvedScopes(params) {
152
228
  return scopes;
153
229
  }
154
230
  /**
231
+ * Read a workspace package's raw package.json. A genuinely missing manifest
232
+ * resolves to `null` so the caller can skip that package — a workspace
233
+ * directory without a `package.json` is not a release surface. Any OTHER
234
+ * failure (unreadable file, malformed JSON) is surfaced as a
235
+ * {@link ConfigurationError} carrying the package path, rather than silently
236
+ * dropping the package, which would cascade into wrong attribution.
237
+ */
238
+ function readRawPackageJson(fs, pkgDir) {
239
+ const pkgJsonPath = join(pkgDir, "package.json");
240
+ return fs.readFileString(pkgJsonPath).pipe(Effect.flatMap((content) => Effect.try(() => JSON.parse(content))), Effect.catchAll((err) => {
241
+ if (err.reason === "NotFound") return Effect.succeed(null);
242
+ return Effect.fail(new ConfigurationError({
243
+ field: "workspace",
244
+ reason: `Failed to read or parse ${pkgJsonPath}: ${err instanceof Error ? err.message : String(err)}`
245
+ }));
246
+ }));
247
+ }
248
+ /**
249
+ * Build resolved scopes from the discovered workspace packages, keeping only
250
+ * those that are a release surface (declare publishConfig that yields at least
251
+ * one publish target). Used when `.changeset/config.json` declares no explicit
252
+ * `packages` record. The changeset `ignore` list is intentionally NOT consulted
253
+ * here — an ignored package still declares publishConfig and remains a valid
254
+ * changeset target.
255
+ */
256
+ function buildFallbackScopes(fs, workspaces) {
257
+ return Effect.forEach(workspaces, (ws) => Effect.gen(function* () {
258
+ const raw = yield* readRawPackageJson(fs, ws.path);
259
+ if (raw === null) return [];
260
+ const binding = yield* readTargetsBinding(fs, ws.path);
261
+ if (SilkPublishability.detect(ws.name, raw, binding).length === 0) return [];
262
+ return [{
263
+ name: ws.name,
264
+ workspaceDir: ws.path,
265
+ version: ws.version,
266
+ additionalScopes: [],
267
+ additionalScopeFiles: [],
268
+ versionFiles: []
269
+ }];
270
+ })).pipe(Effect.map((nested) => nested.flat()));
271
+ }
272
+ /**
155
273
  * Cross-package validation: no overlap in `additionalScopes`, no shadowing
156
274
  * of another workspace package's directory (regardless of whether that
157
275
  * package is itself declared in `config.packages`), and no duplicate
@@ -209,7 +327,7 @@ function configErrorFromParseError(parseError, configPath) {
209
327
  * Each shape carries a private cache keyed by absolute project dir so
210
328
  * repeat `inspect`/`classify` calls reuse the materialized state.
211
329
  */
212
- function makeShape(reader, discovery) {
330
+ function makeShape(reader, discovery, fs) {
213
331
  const cache = /* @__PURE__ */ new Map();
214
332
  const inspect = (cwd) => Effect.gen(function* () {
215
333
  const projectDir = resolve(cwd);
@@ -245,8 +363,9 @@ function makeShape(reader, discovery) {
245
363
  path: w.path,
246
364
  version: w.version
247
365
  }));
366
+ const hasExplicitPackages = Object.keys(decodedOptions.packages ?? {}).length > 0;
248
367
  let scopes;
249
- try {
368
+ if (hasExplicitPackages) try {
250
369
  scopes = buildResolvedScopes({
251
370
  options: decodedOptions,
252
371
  workspaces,
@@ -258,6 +377,7 @@ function makeShape(reader, discovery) {
258
377
  if (e instanceof ConfigurationError) return yield* Effect.fail(e);
259
378
  throw e;
260
379
  }
380
+ else scopes = yield* buildFallbackScopes(fs, workspaces);
261
381
  const inspected = {
262
382
  configPath,
263
383
  projectDir,
@@ -332,7 +452,7 @@ function classifyOne(inspected, path) {
332
452
  * @public
333
453
  */
334
454
  const ConfigInspectorLive = Layer.effect(ConfigInspector, Effect.gen(function* () {
335
- return makeShape(yield* ChangesetConfigReader, yield* WorkspaceDiscovery);
455
+ return makeShape(yield* ChangesetConfigReader, yield* WorkspaceDiscovery, yield* FileSystem.FileSystem);
336
456
  }));
337
457
  /**
338
458
  * Test factory — build a {@link ConfigInspector} that returns a fixed
@@ -352,4 +472,4 @@ function makeConfigInspectorTest(fixed) {
352
472
  }
353
473
 
354
474
  //#endregion
355
- export { ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, makeConfigInspectorTest };
475
+ export { ClassificationReasonSchema, ClassificationSchema, ConfigInspector, ConfigInspectorBase, ConfigInspectorLive, InspectedConfigSchema, ResolvedPackageScopeSchema, ResolvedVersionFileSchema, makeConfigInspectorTest };