@savvy-web/silk-effects 0.6.1 → 1.0.1

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.
Files changed (155) hide show
  1. package/README.md +48 -17
  2. package/_virtual/_rolldown/runtime.js +18 -0
  3. package/changesets/api/categories.js +247 -0
  4. package/changesets/api/changelog.js +134 -0
  5. package/changesets/api/dependency-table.js +163 -0
  6. package/changesets/api/linter.js +168 -0
  7. package/changesets/api/transformer.js +140 -0
  8. package/changesets/categories/index.js +299 -0
  9. package/changesets/categories/types.js +66 -0
  10. package/changesets/changelog/formatting.js +119 -0
  11. package/changesets/changelog/getDependencyReleaseLine.js +114 -0
  12. package/changesets/changelog/getReleaseLine.js +122 -0
  13. package/changesets/changelog/index.js +99 -0
  14. package/changesets/constants.js +43 -0
  15. package/changesets/errors.js +305 -0
  16. package/changesets/index.js +146 -0
  17. package/changesets/markdownlint/index.js +29 -0
  18. package/changesets/markdownlint/rules/content-structure.js +98 -0
  19. package/changesets/markdownlint/rules/dependency-table-format.js +170 -0
  20. package/changesets/markdownlint/rules/heading-hierarchy.js +61 -0
  21. package/changesets/markdownlint/rules/required-sections.js +54 -0
  22. package/changesets/markdownlint/rules/uncategorized-content.js +54 -0
  23. package/changesets/markdownlint/rules/utils.js +30 -0
  24. package/changesets/remark/plugins/aggregate-dependency-tables.js +47 -0
  25. package/changesets/remark/plugins/contributor-footnotes.js +123 -0
  26. package/changesets/remark/plugins/deduplicate-items.js +30 -0
  27. package/changesets/remark/plugins/issue-link-refs.js +58 -0
  28. package/changesets/remark/plugins/merge-sections.js +43 -0
  29. package/changesets/remark/plugins/normalize-format.js +47 -0
  30. package/changesets/remark/plugins/reorder-sections.js +34 -0
  31. package/changesets/remark/presets.js +119 -0
  32. package/changesets/remark/rules/content-structure.js +22 -0
  33. package/changesets/remark/rules/dependency-table-format.js +40 -0
  34. package/changesets/remark/rules/heading-hierarchy.js +19 -0
  35. package/changesets/remark/rules/required-sections.js +17 -0
  36. package/changesets/remark/rules/uncategorized-content.js +31 -0
  37. package/changesets/schemas/changeset.js +146 -0
  38. package/changesets/schemas/dependency-table.js +189 -0
  39. package/changesets/schemas/git.js +69 -0
  40. package/changesets/schemas/github.js +175 -0
  41. package/changesets/schemas/options.js +182 -0
  42. package/changesets/schemas/package-scope.js +128 -0
  43. package/changesets/schemas/primitives.js +72 -0
  44. package/changesets/schemas/version-files.js +151 -0
  45. package/changesets/services/branch-analyzer.js +278 -0
  46. package/changesets/services/changelog.js +50 -0
  47. package/changesets/services/config-inspector.js +390 -0
  48. package/changesets/services/github.js +178 -0
  49. package/changesets/services/markdown.js +106 -0
  50. package/changesets/services/workspace-snapshot.js +182 -0
  51. package/changesets/utils/commit-parser.js +80 -0
  52. package/changesets/utils/dep-diff.js +77 -0
  53. package/changesets/utils/dependency-table.js +347 -0
  54. package/changesets/utils/issue-refs.js +101 -0
  55. package/changesets/utils/jsonpath.js +175 -0
  56. package/changesets/utils/logger.js +50 -0
  57. package/changesets/utils/markdown-link.js +57 -0
  58. package/changesets/utils/publishability.js +39 -0
  59. package/changesets/utils/remark-pipeline.js +79 -0
  60. package/changesets/utils/section-parser.js +94 -0
  61. package/changesets/utils/strip-frontmatter.js +46 -0
  62. package/changesets/utils/version-blocks.js +108 -0
  63. package/changesets/utils/version-files.js +336 -0
  64. package/changesets/utils/worktree-snapshot.js +142 -0
  65. package/changesets/vendor/github-info.js +55 -0
  66. package/commitlint/config/factory.js +69 -0
  67. package/commitlint/config/plugins.js +227 -0
  68. package/commitlint/config/rules.js +155 -0
  69. package/commitlint/config/schema.js +46 -0
  70. package/commitlint/detection/dco.js +53 -0
  71. package/commitlint/detection/scopes.js +45 -0
  72. package/commitlint/formatter/format.js +85 -0
  73. package/commitlint/formatter/messages.js +79 -0
  74. package/commitlint/hook/diagnostics/branch.js +36 -0
  75. package/commitlint/hook/diagnostics/cache.js +37 -0
  76. package/commitlint/hook/diagnostics/commitlint-config.js +36 -0
  77. package/commitlint/hook/diagnostics/open-issues.js +56 -0
  78. package/commitlint/hook/diagnostics/package-manager.js +51 -0
  79. package/commitlint/hook/diagnostics/signing.js +107 -0
  80. package/commitlint/hook/envelope.js +46 -0
  81. package/commitlint/hook/output.js +45 -0
  82. package/commitlint/hook/parse-bash-command.js +105 -0
  83. package/commitlint/hook/rules/closes-trailer.js +31 -0
  84. package/commitlint/hook/rules/forbidden-content.js +32 -0
  85. package/commitlint/hook/rules/plan-leakage.js +36 -0
  86. package/commitlint/hook/rules/signing-flag-conflict.js +25 -0
  87. package/commitlint/hook/rules/soft-wrap.js +37 -0
  88. package/commitlint/hook/rules/types.js +14 -0
  89. package/commitlint/hook/rules/verbosity.js +31 -0
  90. package/commitlint/hook/silence-logger.js +39 -0
  91. package/commitlint/index.js +146 -0
  92. package/commitlint/prompt/config.js +91 -0
  93. package/commitlint/prompt/emojis.js +74 -0
  94. package/commitlint/prompt/prompter.js +135 -0
  95. package/commitlint/static.js +73 -0
  96. package/errors/BiomeSyncError.js +21 -0
  97. package/errors/ChangesetConfigError.js +20 -0
  98. package/errors/ConfigNotFoundError.js +21 -0
  99. package/errors/SectionParseError.js +16 -0
  100. package/errors/SectionValidationError.js +16 -0
  101. package/errors/SectionWriteError.js +16 -0
  102. package/errors/TagFormatError.js +20 -0
  103. package/errors/ToolNotFoundError.js +11 -0
  104. package/errors/ToolResolutionError.js +11 -0
  105. package/errors/ToolVersionMismatchError.js +11 -0
  106. package/errors/VersioningDetectionError.js +20 -0
  107. package/errors/WorkspaceAnalysisError.js +21 -0
  108. package/index.d.ts +9743 -8380
  109. package/index.js +36 -6657
  110. package/lint/Handler.js +39 -0
  111. package/lint/cli/sections.js +65 -0
  112. package/lint/cli/templates/markdownlint.gen.js +183 -0
  113. package/lint/config/Preset.js +152 -0
  114. package/lint/config/createConfig.js +89 -0
  115. package/lint/handlers/Biome.js +179 -0
  116. package/lint/handlers/Markdown.js +139 -0
  117. package/lint/handlers/PackageJson.js +130 -0
  118. package/lint/handlers/PnpmWorkspace.js +141 -0
  119. package/lint/handlers/ShellScripts.js +58 -0
  120. package/lint/handlers/TypeScript.js +134 -0
  121. package/lint/handlers/Yaml.js +167 -0
  122. package/lint/index.js +52 -0
  123. package/lint/utils/Command.js +285 -0
  124. package/lint/utils/Filter.js +100 -0
  125. package/lint/utils/Workspace.js +86 -0
  126. package/package.json +52 -63
  127. package/schemas/CommentStyle.js +16 -0
  128. package/schemas/ResolvedTool.js +63 -0
  129. package/schemas/SavvySections.js +113 -0
  130. package/schemas/SectionBlock.js +70 -0
  131. package/schemas/SectionDefinition.js +121 -0
  132. package/schemas/SectionResults.js +12 -0
  133. package/schemas/TagStrategySchemas.js +18 -0
  134. package/schemas/ToolDefinition.js +39 -0
  135. package/schemas/ToolResults.js +14 -0
  136. package/schemas/VersioningSchemas.js +95 -0
  137. package/schemas/WorkspaceAnalysisSchemas.js +190 -0
  138. package/services/BiomeSchemaSync.js +133 -0
  139. package/services/ChangesetConfig.js +78 -0
  140. package/services/ChangesetConfigReader.js +106 -0
  141. package/services/ConfigDiscovery.js +71 -0
  142. package/services/ManagedSection.js +288 -0
  143. package/services/SilkPublishability.js +193 -0
  144. package/services/SilkWorkspaceAnalyzer.js +213 -0
  145. package/services/TagStrategy.js +54 -0
  146. package/services/ToolDiscovery.js +229 -0
  147. package/services/VersioningStrategy.js +67 -0
  148. package/tsdoc-metadata.json +11 -11
  149. package/turbo/digest.js +127 -0
  150. package/turbo/errors.js +48 -0
  151. package/turbo/index.js +32 -0
  152. package/turbo/schemas/DryRun.js +57 -0
  153. package/turbo/schemas/results.js +61 -0
  154. package/turbo/services/TurboInspector.js +100 -0
  155. package/utils/ToolCommand.js +40 -0
@@ -0,0 +1,69 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/changesets/schemas/git.ts
4
+ /**
5
+ * Git-related Effect schemas for commit hashes and version bump types.
6
+ *
7
+ * @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ /**
12
+ * Schema for a git commit hash (at least 7 lowercase hex characters).
13
+ *
14
+ * @remarks
15
+ * Accepts both abbreviated (7-character) and full (40-character) SHA-1 hashes.
16
+ * Only lowercase hexadecimal characters are allowed; uppercase letters will
17
+ * fail validation. This matches the output of `git rev-parse --short` and
18
+ * `git log --format=%h`.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * import { Schema } from "effect";
23
+ * import { CommitHashSchema } from "@savvy-web/changesets";
24
+ *
25
+ * // Succeeds — abbreviated hash
26
+ * const short = Schema.decodeUnknownSync(CommitHashSchema)("a1b2c3d");
27
+ *
28
+ * // Succeeds — full 40-character SHA
29
+ * const full = Schema.decodeUnknownSync(CommitHashSchema)(
30
+ * "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
31
+ * );
32
+ *
33
+ * // Throws ParseError — too short
34
+ * Schema.decodeUnknownSync(CommitHashSchema)("a1b2c3");
35
+ *
36
+ * // Throws ParseError — uppercase not allowed
37
+ * Schema.decodeUnknownSync(CommitHashSchema)("A1B2C3D");
38
+ * ```
39
+ *
40
+ * @see {@link ChangesetSchema} which uses this for the optional `commit` field
41
+ *
42
+ * @public
43
+ */
44
+ const CommitHashSchema = Schema.String.pipe(Schema.pattern(/^[a-f0-9]{7,}$/, { message: () => "Commit hash must be 7 or more lowercase hexadecimal characters (0-9, a-f). Example: \"a1b2c3d\" or a full 40-character SHA like \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\". Uppercase letters are not allowed" }));
45
+ /**
46
+ * Semantic version bump type.
47
+ *
48
+ * @remarks
49
+ * Represents the four possible version bump levels used by Changesets:
50
+ * - `"major"` -- breaking changes (e.g., 1.x.x to 2.0.0)
51
+ * - `"minor"` -- new features (e.g., 1.1.x to 1.2.0)
52
+ * - `"patch"` -- bug fixes (e.g., 1.1.1 to 1.1.2)
53
+ * - `"none"` -- no version bump (internal changes only)
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import { Schema } from "effect";
58
+ * import { VersionTypeSchema } from "@savvy-web/changesets";
59
+ * import type { VersionType } from "@savvy-web/changesets";
60
+ *
61
+ * const bump: VersionType = Schema.decodeUnknownSync(VersionTypeSchema)("minor");
62
+ * ```
63
+ *
64
+ * @public
65
+ */
66
+ const VersionTypeSchema = Schema.Literal("major", "minor", "patch", "none");
67
+
68
+ //#endregion
69
+ export { CommitHashSchema, VersionTypeSchema };
@@ -0,0 +1,175 @@
1
+ import { PositiveInteger } from "./primitives.js";
2
+ import { MARKDOWN_LINK_PATTERN } from "../utils/markdown-link.js";
3
+ import { Schema } from "effect";
4
+
5
+ //#region src/changesets/schemas/github.ts
6
+ /**
7
+ * GitHub-related Effect schemas for usernames, issue numbers, and API responses.
8
+ *
9
+ * @remarks
10
+ * These schemas validate data returned by the `\@changesets/get-github-info`
11
+ * vendor module and enforce GitHub's naming conventions for usernames and
12
+ * issue references.
13
+ *
14
+ * @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ /**
19
+ * Test whether a string is a valid URL.
20
+ *
21
+ * @internal
22
+ */
23
+ function isValidUrl(value) {
24
+ try {
25
+ new URL(value);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+ /**
32
+ * Schema for a GitHub username.
33
+ *
34
+ * @remarks
35
+ * Validates against GitHub's username rules: alphanumeric characters and
36
+ * hyphens only, cannot start or end with a hyphen. Does not enforce the
37
+ * 39-character maximum length since GitHub may change that limit.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * import { Schema } from "effect";
42
+ * import { UsernameSchema } from "@savvy-web/changesets";
43
+ *
44
+ * // Succeeds
45
+ * Schema.decodeUnknownSync(UsernameSchema)("octocat");
46
+ * Schema.decodeUnknownSync(UsernameSchema)("my-user-123");
47
+ *
48
+ * // Throws ParseError — starts with hyphen
49
+ * Schema.decodeUnknownSync(UsernameSchema)("-invalid");
50
+ * ```
51
+ *
52
+ * @see {@link GitHubInfoSchema} which uses this for the `user` field
53
+ *
54
+ * @public
55
+ */
56
+ const UsernameSchema = Schema.String.pipe(Schema.pattern(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, { message: () => "Invalid GitHub username format. Usernames must contain only alphanumeric characters and hyphens, and cannot start or end with a hyphen. Example: \"octocat\" or \"my-user-123\"" }));
57
+ /**
58
+ * Schema for a GitHub issue or PR number (positive integer).
59
+ *
60
+ * @remarks
61
+ * Built on {@link PositiveInteger}, this schema adds GitHub-specific
62
+ * annotations for documentation tooling. Issue and PR numbers in GitHub
63
+ * are always positive integers starting from 1.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * import { Schema } from "effect";
68
+ * import { IssueNumberSchema } from "@savvy-web/changesets";
69
+ *
70
+ * // Succeeds
71
+ * const prNum = Schema.decodeUnknownSync(IssueNumberSchema)(42);
72
+ *
73
+ * // Throws ParseError — zero is not a valid issue number
74
+ * Schema.decodeUnknownSync(IssueNumberSchema)(0);
75
+ * ```
76
+ *
77
+ * @see {@link GitHubInfoSchema} which uses this for the `pull` field
78
+ *
79
+ * @public
80
+ */
81
+ const IssueNumberSchema = PositiveInteger.annotations({
82
+ title: "IssueNumber",
83
+ description: "GitHub issue or pull request number"
84
+ });
85
+ /**
86
+ * Schema accepting either a plain URL or a markdown link `[text](url)`.
87
+ *
88
+ * @remarks
89
+ * The `\@changesets/get-github-info` vendor module returns links in two
90
+ * possible formats depending on context: a bare URL string or a markdown
91
+ * link like `[#42](https://github.com/owner/repo/pull/42)`. This schema
92
+ * accepts both, validating that the URL portion is parseable by the
93
+ * `URL` constructor.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * import { Schema } from "effect";
98
+ * import { UrlOrMarkdownLinkSchema } from "@savvy-web/changesets";
99
+ *
100
+ * // Succeeds — plain URL
101
+ * Schema.decodeUnknownSync(UrlOrMarkdownLinkSchema)(
102
+ * "https://github.com/owner/repo/pull/42"
103
+ * );
104
+ *
105
+ * // Succeeds — markdown link
106
+ * Schema.decodeUnknownSync(UrlOrMarkdownLinkSchema)(
107
+ * "[#42](https://github.com/owner/repo/pull/42)"
108
+ * );
109
+ *
110
+ * // Throws ParseError — not a URL or markdown link
111
+ * Schema.decodeUnknownSync(UrlOrMarkdownLinkSchema)("not-a-url");
112
+ * ```
113
+ *
114
+ * @see {@link GitHubInfoSchema} which uses this for commit, pull, and user links
115
+ *
116
+ * @public
117
+ */
118
+ const UrlOrMarkdownLinkSchema = Schema.String.pipe(Schema.filter((value) => {
119
+ if (isValidUrl(value)) return true;
120
+ const match = MARKDOWN_LINK_PATTERN.exec(value);
121
+ return match?.[2] ? isValidUrl(match[2]) : false;
122
+ }, { message: () => "Value must be a valid URL or a markdown link. Expected a plain URL (e.g., \"https://github.com/owner/repo/pull/42\") or a markdown link (e.g., \"[#42](https://github.com/owner/repo/pull/42)\")" }));
123
+ /**
124
+ * Schema for a GitHub info response from `\@changesets/get-github-info`.
125
+ *
126
+ * @remarks
127
+ * Represents the structured data returned when querying GitHub for commit
128
+ * metadata. The `user` and `pull` fields are optional because not every
129
+ * commit is associated with a pull request or a known GitHub user (e.g.,
130
+ * bot commits or squash-merged commits without a linked PR).
131
+ *
132
+ * The `links` object contains pre-formatted markdown or URL strings for
133
+ * the commit, pull request, and user profile -- ready for insertion into
134
+ * CHANGELOG entries.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * import { Schema } from "effect";
139
+ * import { GitHubInfoSchema } from "@savvy-web/changesets";
140
+ * import type { GitHubInfo } from "@savvy-web/changesets";
141
+ *
142
+ * const info: GitHubInfo = Schema.decodeUnknownSync(GitHubInfoSchema)({
143
+ * user: "octocat",
144
+ * pull: 42,
145
+ * links: {
146
+ * commit: "[`a1b2c3d`](https://github.com/owner/repo/commit/a1b2c3d)",
147
+ * pull: "[#42](https://github.com/owner/repo/pull/42)",
148
+ * user: "[@octocat](https://github.com/octocat)",
149
+ * },
150
+ * });
151
+ * ```
152
+ *
153
+ * @see {@link GitHubInfo} for the inferred TypeScript type
154
+ * @see {@link GitHubService} for the Effect service that produces these values
155
+ *
156
+ * @public
157
+ */
158
+ const GitHubInfoSchema = Schema.Struct({
159
+ /** GitHub username of the commit author. */
160
+ user: Schema.optional(UsernameSchema),
161
+ /** Pull request number associated with the commit. */
162
+ pull: Schema.optional(IssueNumberSchema),
163
+ /** Markdown-formatted links. */
164
+ links: Schema.Struct({
165
+ /** Link to the commit. */
166
+ commit: UrlOrMarkdownLinkSchema,
167
+ /** Link to the associated pull request. */
168
+ pull: Schema.optional(UrlOrMarkdownLinkSchema),
169
+ /** Link to the author's GitHub profile. */
170
+ user: Schema.optional(UrlOrMarkdownLinkSchema)
171
+ })
172
+ });
173
+
174
+ //#endregion
175
+ export { GitHubInfoSchema, IssueNumberSchema, UrlOrMarkdownLinkSchema, UsernameSchema };
@@ -0,0 +1,182 @@
1
+ import { ConfigurationError } from "../errors.js";
2
+ import { LegacyVersionFilesSchema } from "./version-files.js";
3
+ import { PackagesRecordSchema } from "./package-scope.js";
4
+ import { Effect, Schema } from "effect";
5
+
6
+ //#region src/changesets/schemas/options.ts
7
+ /**
8
+ * Changeset configuration option schemas and validation.
9
+ *
10
+ * @remarks
11
+ * Defines the schema for the options object passed to the
12
+ * \@savvy-web/changesets changelog formatter in `.changeset/config.json`.
13
+ * The options are specified as the second element of the changelog array:
14
+ *
15
+ * ```json
16
+ * {
17
+ * "changelog": ["@savvy-web/changesets/changelog", { "repo": "owner/repo" }]
18
+ * }
19
+ * ```
20
+ *
21
+ * @see {@link https://github.com/changesets/changesets/blob/main/docs/config-file-options.md | Changesets config docs}
22
+ * @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
23
+ *
24
+ * @packageDocumentation
25
+ */
26
+ /** Regex for `owner/repo` format, shared between schema and validation. */
27
+ const REPO_PATTERN = /^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/;
28
+ /**
29
+ * Schema for a GitHub repository in `owner/repo` format.
30
+ *
31
+ * @remarks
32
+ * Validates that the string matches the `owner/repository` format used
33
+ * by GitHub. Both the owner and repository segments accept alphanumeric
34
+ * characters, dots, underscores, and hyphens.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { Schema } from "effect";
39
+ * import { RepoSchema } from "@savvy-web/changesets";
40
+ *
41
+ * // Succeeds
42
+ * Schema.decodeUnknownSync(RepoSchema)("microsoft/vscode");
43
+ *
44
+ * // Throws ParseError — missing slash
45
+ * Schema.decodeUnknownSync(RepoSchema)("vscode");
46
+ * ```
47
+ *
48
+ * @see {@link ChangesetOptionsSchema} which uses this for the `repo` field
49
+ *
50
+ * @public
51
+ */
52
+ const RepoSchema = Schema.String.pipe(Schema.pattern(REPO_PATTERN, { message: () => "Repository must be in format \"owner/repository\" (e.g., \"microsoft/vscode\")" }));
53
+ /**
54
+ * Schema for changeset configuration options.
55
+ *
56
+ * @remarks
57
+ * The `repo` field is required; all other fields are optional with sensible
58
+ * defaults applied by the changelog formatter at runtime.
59
+ *
60
+ * Two related fields describe release surfaces:
61
+ *
62
+ * - `packages` (new, recommended) — a record keyed by package name with
63
+ * per-package `additionalScopes` and `versionFiles`. This is the shape
64
+ * introduced in 0.9.0 and the only shape that will be accepted in 1.0.0.
65
+ * - `versionFiles` (deprecated) — a flat array of entries each carrying
66
+ * their own `package` field. Still accepted in 0.9.0 with a deprecation
67
+ * warning emitted by `ConfigInspector` at config-load time; removed in
68
+ * 1.0.0.
69
+ *
70
+ * A config that declares **both** `packages` and the deprecated
71
+ * `versionFiles` field is rejected — the user must pick one.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * import { Schema } from "effect";
76
+ * import { ChangesetOptionsSchema } from "@savvy-web/changesets";
77
+ * import type { ChangesetOptions } from "@savvy-web/changesets";
78
+ *
79
+ * // New per-package shape (recommended)
80
+ * const opts: ChangesetOptions = Schema.decodeUnknownSync(ChangesetOptionsSchema)({
81
+ * repo: "savvy-web/changesets",
82
+ * packages: {
83
+ * "@savvy-web/changesets": {
84
+ * additionalScopes: ["plugin/**"],
85
+ * versionFiles: [{ glob: "plugin/.claude-plugin/plugin.json", paths: ["$.version"] }],
86
+ * },
87
+ * },
88
+ * });
89
+ * ```
90
+ *
91
+ * @see {@link ChangesetOptions} for the inferred TypeScript type
92
+ * @see {@link validateChangesetOptions} for Effect-idiomatic validation with detailed error messages
93
+ * @see {@link PackagesRecordSchema} for the new `packages` shape
94
+ * @see {@link LegacyVersionFilesSchema} for the deprecated `versionFiles` shape
95
+ *
96
+ * @public
97
+ */
98
+ const ChangesetOptionsSchema = Schema.Struct({
99
+ /** GitHub repository in `owner/repo` format. */
100
+ repo: RepoSchema,
101
+ /** Whether to include commit hash links in output. */
102
+ commitLinks: Schema.optional(Schema.Boolean),
103
+ /** Whether to include pull request links in output. */
104
+ prLinks: Schema.optional(Schema.Boolean),
105
+ /** Whether to include issue reference links in output. */
106
+ issueLinks: Schema.optional(Schema.Boolean),
107
+ /** Custom issue reference prefixes (e.g., `["#", "GH-"]`). */
108
+ issuePrefixes: Schema.optional(Schema.Array(Schema.String)),
109
+ /**
110
+ * Per-package release surfaces. Each entry declares `additionalScopes`
111
+ * (globs outside the workspace dir that belong to the package) and
112
+ * `versionFiles` (files bumped in lockstep with the package's version).
113
+ */
114
+ packages: Schema.optional(PackagesRecordSchema),
115
+ /**
116
+ * DEPRECATED. Top-level flat array of version-file entries each with
117
+ * their own `package` field. Migrate to `packages[<name>].versionFiles`.
118
+ * Removed in 1.0.0.
119
+ *
120
+ * @deprecated 0.9.0 — migrate to `packages`. Removed in 1.0.0.
121
+ */
122
+ versionFiles: Schema.optional(LegacyVersionFilesSchema)
123
+ }).pipe(Schema.filter((opts) => opts.packages === void 0 || opts.versionFiles === void 0, { message: () => "Configuration cannot declare both `packages` and the deprecated top-level `versionFiles` array. Migrate the legacy `versionFiles` entries into `packages[<name>].versionFiles` and remove the top-level field." }));
124
+ /**
125
+ * Validate changeset options with Effect-idiomatic error handling.
126
+ *
127
+ * @remarks
128
+ * Provides detailed, user-friendly error messages that match the prior art
129
+ * from \@savvy-web/changelog. Performs manual pre-validation for `null`,
130
+ * non-object, and missing `repo` before delegating to schema decoding.
131
+ * This layered approach ensures that the most common misconfiguration
132
+ * (missing `repo`) produces a clear, actionable message rather than a
133
+ * generic schema parse error.
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * import { Effect } from "effect";
138
+ * import { validateChangesetOptions, ConfigurationError } from "@savvy-web/changesets";
139
+ *
140
+ * const program = validateChangesetOptions({ repo: "savvy-web/changesets" }).pipe(
141
+ * Effect.catchTag("ConfigurationError", (err) =>
142
+ * Effect.logError(`Config invalid: ${err.message}`)
143
+ * ),
144
+ * );
145
+ *
146
+ * Effect.runPromise(program);
147
+ * ```
148
+ *
149
+ * @param input - Raw configuration input (typically from `.changeset/config.json`)
150
+ * @returns An Effect that succeeds with decoded {@link ChangesetOptions} or fails with a {@link ConfigurationError}
151
+ *
152
+ * @see {@link ChangesetOptionsSchema} for the expected configuration shape
153
+ * @see {@link ConfigurationError} for the error type produced on validation failure
154
+ *
155
+ * @public
156
+ */
157
+ function validateChangesetOptions(input) {
158
+ if (input === null || input === void 0) return Effect.fail(new ConfigurationError({
159
+ field: "options",
160
+ reason: "Configuration is required. Add options to your changesets config:\n\"changelog\": [\"@savvy-web/changesets\", { \"repo\": \"owner/repository\" }]"
161
+ }));
162
+ if (typeof input !== "object") return Effect.fail(new ConfigurationError({
163
+ field: "options",
164
+ reason: "Configuration must be an object"
165
+ }));
166
+ const obj = input;
167
+ if (!("repo" in obj) || obj.repo === void 0) return Effect.fail(new ConfigurationError({
168
+ field: "repo",
169
+ reason: "Repository name is required. Add the \"repo\" option to your changesets config:\n\"changelog\": [\"@savvy-web/changesets\", { \"repo\": \"owner/repository\" }]"
170
+ }));
171
+ if (typeof obj.repo === "string" && !REPO_PATTERN.test(obj.repo)) return Effect.fail(new ConfigurationError({
172
+ field: "repo",
173
+ reason: `Invalid repository format: "${obj.repo}". Expected format is "owner/repository" (e.g., "microsoft/vscode")`
174
+ }));
175
+ return Schema.decodeUnknown(ChangesetOptionsSchema)(input).pipe(Effect.mapError((parseError) => new ConfigurationError({
176
+ field: "options",
177
+ reason: String(parseError)
178
+ })));
179
+ }
180
+
181
+ //#endregion
182
+ export { ChangesetOptionsSchema, RepoSchema, validateChangesetOptions };
@@ -0,0 +1,128 @@
1
+ import { VersionFileConfigSchema } from "./version-files.js";
2
+ import { Schema } from "effect";
3
+
4
+ //#region src/changesets/schemas/package-scope.ts
5
+ /**
6
+ * Schemas for the per-package release-surface configuration introduced in
7
+ * \@savvy-web/changesets 0.9.0.
8
+ *
9
+ * @remarks
10
+ * A `PackageScope` is the value side of the `packages` record on the
11
+ * \@savvy-web/changesets changelog options. It tells the formatter which
12
+ * non-workspace paths belong to a package's release surface (via
13
+ * `additionalScopes`) and which files have their version fields bumped in
14
+ * lockstep with the package (via `versionFiles`).
15
+ *
16
+ * ```jsonc
17
+ * "changelog": [
18
+ * "@savvy-web/changesets/changelog",
19
+ * {
20
+ * "repo": "savvy-web/changesets",
21
+ * "packages": {
22
+ * "@savvy-web/changesets": {
23
+ * "additionalScopes": ["plugin/**"],
24
+ * "versionFiles": [
25
+ * { "glob": "plugin/.claude-plugin/plugin.json",
26
+ * "paths": ["$.version"] }
27
+ * ]
28
+ * }
29
+ * }
30
+ * }
31
+ * ]
32
+ * ```
33
+ *
34
+ * Validation contracts that span multiple packages (no-overlap rules for
35
+ * `additionalScopes`, no glob+path conflicts in `versionFiles`, unknown
36
+ * package keys) are enforced by `ConfigInspector` at load time, not at the
37
+ * schema level — schemas only validate per-entry shape.
38
+ *
39
+ * @see {@link ChangesetOptionsSchema} for the consuming options shape
40
+ * @see {@link VersionFileConfigSchema} for the version-file entry shape
41
+ * @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
42
+ *
43
+ * @packageDocumentation
44
+ */
45
+ /**
46
+ * Schema for a single repo-relative glob pattern.
47
+ *
48
+ * @remarks
49
+ * Globs in `additionalScopes` must be repo-relative — absolute paths and
50
+ * parent traversal (`../`) are rejected outright because they cannot be
51
+ * meaningfully resolved against the project workspace. Negation patterns
52
+ * (`!path/**`) are accepted; minimatch handles them at match time.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * import { Schema } from "effect";
57
+ * import { GlobSchema } from "@savvy-web/changesets";
58
+ *
59
+ * Schema.decodeUnknownSync(GlobSchema)("plugin/**"); // ok
60
+ * Schema.decodeUnknownSync(GlobSchema)("!plugin/cache/**"); // ok (negation)
61
+ * Schema.decodeUnknownSync(GlobSchema)("/absolute/path"); // throws
62
+ * Schema.decodeUnknownSync(GlobSchema)("../sibling/**"); // throws
63
+ * ```
64
+ *
65
+ * @public
66
+ */
67
+ const GlobSchema = Schema.String.pipe(Schema.minLength(1), Schema.filter((s) => !s.startsWith("/") && !s.startsWith("../") && !s.includes("/../"), { message: () => "Glob must be repo-relative — absolute paths and parent traversal (../) are not allowed" }));
68
+ /**
69
+ * Schema for a single entry in the `packages` record — the per-package
70
+ * release-surface declaration.
71
+ *
72
+ * @remarks
73
+ * Both fields are optional. An empty object (`{}`) is a valid PackageScope
74
+ * and means "this package uses only its workspace directory as its release
75
+ * surface."
76
+ *
77
+ * - `additionalScopes` — repo-relative globs naming files outside the
78
+ * package's workspace directory that belong to the package's release
79
+ * surface. The contract is "a path belongs to exactly one package" — if
80
+ * two packages declare overlapping additionalScopes, the config is
81
+ * rejected by `ConfigInspector`.
82
+ * - `versionFiles` — files whose JSON fields are bumped in lockstep with
83
+ * this package's version. Each entry uses the new {@link VersionFileConfigSchema}
84
+ * shape (no `package` field; the parent record key names the owner).
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * import { Schema } from "effect";
89
+ * import { PackageScopeSchema } from "@savvy-web/changesets";
90
+ *
91
+ * const scope = Schema.decodeUnknownSync(PackageScopeSchema)({
92
+ * additionalScopes: ["plugin/**", "!plugin/.cache/**"],
93
+ * versionFiles: [
94
+ * { glob: "plugin/.claude-plugin/plugin.json", paths: ["$.version"] },
95
+ * ],
96
+ * });
97
+ * ```
98
+ *
99
+ * @see {@link PackageScope} for the inferred TypeScript type
100
+ * @see {@link GlobSchema} for individual glob validation rules
101
+ * @see {@link VersionFileConfigSchema} for the version-file entry shape
102
+ *
103
+ * @public
104
+ */
105
+ const PackageScopeSchema = Schema.Struct({
106
+ /** Repo-relative globs naming files outside the package's workspace directory that belong to its release surface. */
107
+ additionalScopes: Schema.optional(Schema.Array(GlobSchema)),
108
+ /** Files whose JSON fields are bumped in lockstep with this package's version. */
109
+ versionFiles: Schema.optional(Schema.Array(VersionFileConfigSchema))
110
+ });
111
+ /**
112
+ * Schema for the `packages` record on the changelog options.
113
+ *
114
+ * @remarks
115
+ * Keys are package names. The schema itself does not validate that keys
116
+ * resolve to known workspace packages — that check is performed by
117
+ * `ConfigInspector` at config-load time, where `WorkspaceDiscovery` is
118
+ * available to enumerate the workspace.
119
+ *
120
+ * @public
121
+ */
122
+ const PackagesRecordSchema = Schema.Record({
123
+ key: Schema.String,
124
+ value: PackageScopeSchema
125
+ });
126
+
127
+ //#endregion
128
+ export { GlobSchema, PackageScopeSchema, PackagesRecordSchema };
@@ -0,0 +1,72 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/changesets/schemas/primitives.ts
4
+ /**
5
+ * Primitive reusable schemas for common constrained types.
6
+ *
7
+ * @remarks
8
+ * These schemas serve as building blocks for more complex schemas throughout
9
+ * the \@savvy-web/changesets package. They encode common constraints
10
+ * (non-empty strings, positive integers) as Effect Schemas so that validation
11
+ * is enforced at system boundaries.
12
+ *
13
+ * @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ /**
18
+ * A non-empty string schema.
19
+ *
20
+ * @remarks
21
+ * Validates that a string has at least one character. Used as the base
22
+ * constraint for package names, dependency identifiers, and other fields
23
+ * that must not be blank.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import { Schema } from "effect";
28
+ * import { NonEmptyString } from "@savvy-web/changesets";
29
+ *
30
+ * // Succeeds — non-empty string
31
+ * const name = Schema.decodeUnknownSync(NonEmptyString)("react");
32
+ *
33
+ * // Throws ParseError — empty string
34
+ * Schema.decodeUnknownSync(NonEmptyString)("");
35
+ * ```
36
+ *
37
+ * @see {@link ChangesetSummarySchema} for a length-bounded variant used for changeset summaries
38
+ *
39
+ * @public
40
+ */
41
+ const NonEmptyString = Schema.String.pipe(Schema.minLength(1));
42
+ /**
43
+ * A positive integer schema.
44
+ *
45
+ * @remarks
46
+ * Validates that a number is both an integer and strictly greater than zero.
47
+ * Used as the base constraint for GitHub issue numbers and similar
48
+ * identifiers that must be positive whole numbers.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * import { Schema } from "effect";
53
+ * import { PositiveInteger } from "@savvy-web/changesets";
54
+ *
55
+ * // Succeeds — positive integer
56
+ * const issueNum = Schema.decodeUnknownSync(PositiveInteger)(42);
57
+ *
58
+ * // Throws ParseError — zero is not positive
59
+ * Schema.decodeUnknownSync(PositiveInteger)(0);
60
+ *
61
+ * // Throws ParseError — floats are not integers
62
+ * Schema.decodeUnknownSync(PositiveInteger)(3.14);
63
+ * ```
64
+ *
65
+ * @see {@link IssueNumberSchema} which builds on this for GitHub issue/PR numbers
66
+ *
67
+ * @public
68
+ */
69
+ const PositiveInteger = Schema.Number.pipe(Schema.int(), Schema.positive());
70
+
71
+ //#endregion
72
+ export { NonEmptyString, PositiveInteger };