@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.
- package/README.md +48 -17
- package/_virtual/_rolldown/runtime.js +18 -0
- package/changesets/api/categories.js +247 -0
- package/changesets/api/changelog.js +134 -0
- package/changesets/api/dependency-table.js +163 -0
- package/changesets/api/linter.js +168 -0
- package/changesets/api/transformer.js +140 -0
- package/changesets/categories/index.js +299 -0
- package/changesets/categories/types.js +66 -0
- package/changesets/changelog/formatting.js +119 -0
- package/changesets/changelog/getDependencyReleaseLine.js +114 -0
- package/changesets/changelog/getReleaseLine.js +122 -0
- package/changesets/changelog/index.js +99 -0
- package/changesets/constants.js +43 -0
- package/changesets/errors.js +305 -0
- package/changesets/index.js +146 -0
- package/changesets/markdownlint/index.js +29 -0
- package/changesets/markdownlint/rules/content-structure.js +98 -0
- package/changesets/markdownlint/rules/dependency-table-format.js +170 -0
- package/changesets/markdownlint/rules/heading-hierarchy.js +61 -0
- package/changesets/markdownlint/rules/required-sections.js +54 -0
- package/changesets/markdownlint/rules/uncategorized-content.js +54 -0
- package/changesets/markdownlint/rules/utils.js +30 -0
- package/changesets/remark/plugins/aggregate-dependency-tables.js +47 -0
- package/changesets/remark/plugins/contributor-footnotes.js +123 -0
- package/changesets/remark/plugins/deduplicate-items.js +30 -0
- package/changesets/remark/plugins/issue-link-refs.js +58 -0
- package/changesets/remark/plugins/merge-sections.js +43 -0
- package/changesets/remark/plugins/normalize-format.js +47 -0
- package/changesets/remark/plugins/reorder-sections.js +34 -0
- package/changesets/remark/presets.js +119 -0
- package/changesets/remark/rules/content-structure.js +22 -0
- package/changesets/remark/rules/dependency-table-format.js +40 -0
- package/changesets/remark/rules/heading-hierarchy.js +19 -0
- package/changesets/remark/rules/required-sections.js +17 -0
- package/changesets/remark/rules/uncategorized-content.js +31 -0
- package/changesets/schemas/changeset.js +146 -0
- package/changesets/schemas/dependency-table.js +189 -0
- package/changesets/schemas/git.js +69 -0
- package/changesets/schemas/github.js +175 -0
- package/changesets/schemas/options.js +182 -0
- package/changesets/schemas/package-scope.js +128 -0
- package/changesets/schemas/primitives.js +72 -0
- package/changesets/schemas/version-files.js +151 -0
- package/changesets/services/branch-analyzer.js +278 -0
- package/changesets/services/changelog.js +50 -0
- package/changesets/services/config-inspector.js +390 -0
- package/changesets/services/github.js +178 -0
- package/changesets/services/markdown.js +106 -0
- package/changesets/services/workspace-snapshot.js +182 -0
- package/changesets/utils/commit-parser.js +80 -0
- package/changesets/utils/dep-diff.js +77 -0
- package/changesets/utils/dependency-table.js +347 -0
- package/changesets/utils/issue-refs.js +101 -0
- package/changesets/utils/jsonpath.js +175 -0
- package/changesets/utils/logger.js +50 -0
- package/changesets/utils/markdown-link.js +57 -0
- package/changesets/utils/publishability.js +39 -0
- package/changesets/utils/remark-pipeline.js +79 -0
- package/changesets/utils/section-parser.js +94 -0
- package/changesets/utils/strip-frontmatter.js +46 -0
- package/changesets/utils/version-blocks.js +108 -0
- package/changesets/utils/version-files.js +336 -0
- package/changesets/utils/worktree-snapshot.js +142 -0
- package/changesets/vendor/github-info.js +55 -0
- package/commitlint/config/factory.js +69 -0
- package/commitlint/config/plugins.js +227 -0
- package/commitlint/config/rules.js +155 -0
- package/commitlint/config/schema.js +46 -0
- package/commitlint/detection/dco.js +53 -0
- package/commitlint/detection/scopes.js +45 -0
- package/commitlint/formatter/format.js +85 -0
- package/commitlint/formatter/messages.js +79 -0
- package/commitlint/hook/diagnostics/branch.js +36 -0
- package/commitlint/hook/diagnostics/cache.js +37 -0
- package/commitlint/hook/diagnostics/commitlint-config.js +36 -0
- package/commitlint/hook/diagnostics/open-issues.js +56 -0
- package/commitlint/hook/diagnostics/package-manager.js +51 -0
- package/commitlint/hook/diagnostics/signing.js +107 -0
- package/commitlint/hook/envelope.js +46 -0
- package/commitlint/hook/output.js +45 -0
- package/commitlint/hook/parse-bash-command.js +105 -0
- package/commitlint/hook/rules/closes-trailer.js +31 -0
- package/commitlint/hook/rules/forbidden-content.js +32 -0
- package/commitlint/hook/rules/plan-leakage.js +36 -0
- package/commitlint/hook/rules/signing-flag-conflict.js +25 -0
- package/commitlint/hook/rules/soft-wrap.js +37 -0
- package/commitlint/hook/rules/types.js +14 -0
- package/commitlint/hook/rules/verbosity.js +31 -0
- package/commitlint/hook/silence-logger.js +39 -0
- package/commitlint/index.js +146 -0
- package/commitlint/prompt/config.js +91 -0
- package/commitlint/prompt/emojis.js +74 -0
- package/commitlint/prompt/prompter.js +135 -0
- package/commitlint/static.js +73 -0
- package/errors/BiomeSyncError.js +21 -0
- package/errors/ChangesetConfigError.js +20 -0
- package/errors/ConfigNotFoundError.js +21 -0
- package/errors/SectionParseError.js +16 -0
- package/errors/SectionValidationError.js +16 -0
- package/errors/SectionWriteError.js +16 -0
- package/errors/TagFormatError.js +20 -0
- package/errors/ToolNotFoundError.js +11 -0
- package/errors/ToolResolutionError.js +11 -0
- package/errors/ToolVersionMismatchError.js +11 -0
- package/errors/VersioningDetectionError.js +20 -0
- package/errors/WorkspaceAnalysisError.js +21 -0
- package/index.d.ts +9743 -8380
- package/index.js +36 -6657
- package/lint/Handler.js +39 -0
- package/lint/cli/sections.js +65 -0
- package/lint/cli/templates/markdownlint.gen.js +183 -0
- package/lint/config/Preset.js +152 -0
- package/lint/config/createConfig.js +89 -0
- package/lint/handlers/Biome.js +179 -0
- package/lint/handlers/Markdown.js +139 -0
- package/lint/handlers/PackageJson.js +130 -0
- package/lint/handlers/PnpmWorkspace.js +141 -0
- package/lint/handlers/ShellScripts.js +58 -0
- package/lint/handlers/TypeScript.js +134 -0
- package/lint/handlers/Yaml.js +167 -0
- package/lint/index.js +52 -0
- package/lint/utils/Command.js +285 -0
- package/lint/utils/Filter.js +100 -0
- package/lint/utils/Workspace.js +86 -0
- package/package.json +52 -63
- package/schemas/CommentStyle.js +16 -0
- package/schemas/ResolvedTool.js +63 -0
- package/schemas/SavvySections.js +113 -0
- package/schemas/SectionBlock.js +70 -0
- package/schemas/SectionDefinition.js +121 -0
- package/schemas/SectionResults.js +12 -0
- package/schemas/TagStrategySchemas.js +18 -0
- package/schemas/ToolDefinition.js +39 -0
- package/schemas/ToolResults.js +14 -0
- package/schemas/VersioningSchemas.js +95 -0
- package/schemas/WorkspaceAnalysisSchemas.js +190 -0
- package/services/BiomeSchemaSync.js +133 -0
- package/services/ChangesetConfig.js +78 -0
- package/services/ChangesetConfigReader.js +106 -0
- package/services/ConfigDiscovery.js +71 -0
- package/services/ManagedSection.js +288 -0
- package/services/SilkPublishability.js +193 -0
- package/services/SilkWorkspaceAnalyzer.js +213 -0
- package/services/TagStrategy.js +54 -0
- package/services/ToolDiscovery.js +229 -0
- package/services/VersioningStrategy.js +67 -0
- package/tsdoc-metadata.json +11 -11
- package/turbo/digest.js +127 -0
- package/turbo/errors.js +48 -0
- package/turbo/index.js +32 -0
- package/turbo/schemas/DryRun.js +57 -0
- package/turbo/schemas/results.js +61 -0
- package/turbo/services/TurboInspector.js +100 -0
- 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 };
|