@maple-dev/changesets 0.0.0-stage → 0.1.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.
@@ -0,0 +1,2566 @@
1
+ import { Config, Console, Context, Effect, FileSystem, Glob, Layer, Option, Path, Predicate, Random, Result, Schema, SemVer, SemVerRange, Stream } from "effect";
2
+ import { ChildProcess, ChildProcessSpawner } from "effect/process";
3
+ import { Command, Flag, Prompt } from "effect/cli";
4
+ // src/semver.ts
5
+ /**
6
+ * The handful of node-semver operations changesets relies on, over `effect/SemVer` and
7
+ * `effect/SemVerRange`. `rangeComparators` re-implements node-semver's desugaring for the two
8
+ * places where changesets reads a range's structure instead of only matching against it.
9
+ */
10
+ const parseVersion = (version) => version === void 0 ? Result.failVoid : Result.mapError(SemVer.parse(version.trim()), () => void 0);
11
+ const isValidRange = (range) => Result.isSuccess(SemVerRange.parse(range.trim()));
12
+ /** `semver.satisfies`: an invalid version or range never satisfies. */
13
+ const satisfies = (version, range) => {
14
+ if (range === void 0) return false;
15
+ const parsedVersion = parseVersion(version);
16
+ const parsedRange = SemVerRange.parse(range.trim());
17
+ return Result.isSuccess(parsedVersion) && Result.isSuccess(parsedRange) && SemVerRange.matches(parsedRange.success, parsedVersion.success);
18
+ };
19
+ const bumps = {
20
+ major: SemVer.bumpMajor,
21
+ minor: SemVer.bumpMinor,
22
+ patch: SemVer.bumpPatch
23
+ };
24
+ /** `semver.inc` for the three release types; `undefined` for an invalid version, like `null` there. */
25
+ const increment = (version, type) => {
26
+ const parsed = parseVersion(version);
27
+ return Result.isSuccess(parsed) ? SemVer.format(bumps[type](parsed.success)) : void 0;
28
+ };
29
+ const isPrerelease = (version) => {
30
+ const parsed = parseVersion(version);
31
+ return Result.isSuccess(parsed) && SemVer.isPrerelease(parsed.success);
32
+ };
33
+ const greaterThan = (a, b) => {
34
+ const left = parseVersion(a);
35
+ const right = parseVersion(b);
36
+ return Result.isSuccess(left) && Result.isSuccess(right) && SemVer.isGreaterThan(left.success, right.success);
37
+ };
38
+ const lessThan = (a, b) => {
39
+ const left = parseVersion(a);
40
+ const right = parseVersion(b);
41
+ return Result.isSuccess(left) && Result.isSuccess(right) && SemVer.isLessThan(left.success, right.success);
42
+ };
43
+ const isX = (part) => part === void 0 || part === "x" || part === "X" || part === "*";
44
+ const partialPattern = /^v?(\d+|[xX*])?(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
45
+ const operatorPattern = /^(<=|>=|<|>|=|~>|~|\^)?(.*)$/;
46
+ const desugarToken = (operator, version) => {
47
+ const match = partialPattern.exec(version);
48
+ if (match === null) return [{
49
+ operator,
50
+ value: version
51
+ }];
52
+ const [, major, minor, patch, pre] = match;
53
+ if (isX(major)) return operator === "<" || operator === ">" ? [{
54
+ operator: "<",
55
+ value: "<0.0.0-0"
56
+ }] : [];
57
+ const M = Number(major);
58
+ const m = Number(minor);
59
+ const full = `${major}.${minor}.${patch}${pre === void 0 ? "" : `-${pre}`}`;
60
+ switch (operator) {
61
+ case ">": return isX(minor) ? [{
62
+ operator: ">=",
63
+ value: `>=${M + 1}.0.0`
64
+ }] : isX(patch) ? [{
65
+ operator: ">=",
66
+ value: `>=${M}.${m + 1}.0`
67
+ }] : [{
68
+ operator: ">",
69
+ value: `>${full}`
70
+ }];
71
+ case ">=": {
72
+ const value = isX(minor) ? `>=${M}.0.0` : isX(patch) ? `>=${M}.${m}.0` : `>=${full}`;
73
+ return value === ">=0.0.0" ? [] : [{
74
+ operator: ">=",
75
+ value
76
+ }];
77
+ }
78
+ case "<": return [{
79
+ operator: "<",
80
+ value: isX(minor) ? `<${M}.0.0-0` : isX(patch) ? `<${M}.${m}.0-0` : `<${full}`
81
+ }];
82
+ case "<=": return isX(minor) ? [{
83
+ operator: "<",
84
+ value: `<${M + 1}.0.0-0`
85
+ }] : isX(patch) ? [{
86
+ operator: "<",
87
+ value: `<${M}.${m + 1}.0-0`
88
+ }] : [{
89
+ operator: "<=",
90
+ value: `<=${full}`
91
+ }];
92
+ case "":
93
+ case "=": return isX(minor) || isX(patch) ? [{
94
+ operator: ">=",
95
+ value: ">="
96
+ }, {
97
+ operator: "<",
98
+ value: "<"
99
+ }] : [{
100
+ operator: "",
101
+ value: full
102
+ }];
103
+ default: return [{
104
+ operator: ">=",
105
+ value: ">="
106
+ }, {
107
+ operator: "<",
108
+ value: "<"
109
+ }];
110
+ }
111
+ };
112
+ const tokens = (set) => {
113
+ return set.trim().split(/\s+/).filter((part) => part.length > 0).reduce((merged, part) => {
114
+ const previous = merged.at(-1);
115
+ return previous !== void 0 && /^(<=|>=|<|>|=|~>|~|\^)$/.test(previous) ? [...merged.slice(0, -1), `${previous}${part}`] : [...merged, part];
116
+ }, []);
117
+ };
118
+ /** The comparators node-semver builds for one `||`-free comparator set. */
119
+ const setComparators = (set) => {
120
+ const parts = tokens(set);
121
+ if (parts.length === 3 && parts[1] === "-") return isX(partialPattern.exec(parts[0])?.[1]) && isX(partialPattern.exec(parts[2])?.[1]) ? [] : [{
122
+ operator: ">=",
123
+ value: ">="
124
+ }, {
125
+ operator: "<=",
126
+ value: "<="
127
+ }];
128
+ return parts.flatMap((part) => {
129
+ const [, operator = "", version = ""] = operatorPattern.exec(part) ?? [];
130
+ return desugarToken(operator, version);
131
+ });
132
+ };
133
+ /** `new Range(range).range === ""`: some alternative accepts every version. */
134
+ const isAnyRange = (range) => range.split("||").some((set) => setComparators(set).length === 0);
135
+ /** `new Range(range).set`: one comparator list per `||` alternative. */
136
+ const rangeComparators = (range) => range.split("||").map(setComparators);
137
+ // src/changelog.ts
138
+ /**
139
+ * CHANGELOG.md entries byte-for-byte as `@changesets/apply-release-plan` writes them with the
140
+ * default `@changesets/cli/changelog` (which is `@changesets/changelog-git`).
141
+ */
142
+ const shortCommit = (commit) => commit ? commit.slice(0, 7) : void 0;
143
+ /** `@changesets/changelog-git`. */
144
+ const gitChangelog = {
145
+ getReleaseLine: (changeset) => {
146
+ const [firstLine, ...futureLines] = changeset.summary.split("\n").map((line) => line.trimEnd());
147
+ const commit = shortCommit(changeset.commit);
148
+ const head = `- ${commit === void 0 ? "" : `${commit}: `}${firstLine}`;
149
+ return Effect.succeed(futureLines.length > 0 ? `${head}\n${futureLines.map((line) => ` ${line}`).join("\n")}` : head);
150
+ },
151
+ getDependencyReleaseLine: (changesets, dependenciesUpdated) => {
152
+ if (dependenciesUpdated.length === 0) return Effect.succeed("");
153
+ const links = changesets.map((changeset) => {
154
+ const commit = shortCommit(changeset.commit);
155
+ return `- Updated dependencies${commit === void 0 ? "" : ` [${commit}]`}`;
156
+ });
157
+ const list = dependenciesUpdated.map((dependency) => ` - ${dependency.name}@${dependency.newVersion}`);
158
+ return Effect.succeed([...links, ...list].join("\n"));
159
+ }
160
+ };
161
+ const bumpLevel = {
162
+ none: 0,
163
+ patch: 1,
164
+ minor: 2,
165
+ major: 3
166
+ };
167
+ /** `shouldUpdateDependencyBasedOnConfig`. */
168
+ const shouldUpdateDependency = (release, depVersionRange, depType, options) => {
169
+ if (release.newVersion === void 0) return false;
170
+ const workspace = depVersionRange.startsWith("workspace:");
171
+ const stripped = workspace ? depVersionRange.slice(10) : depVersionRange;
172
+ if (workspace && stripped === "*") return true;
173
+ if (workspace && stripped !== "^" && stripped !== "~" && !isValidRange(stripped)) return normalizePath(stripped) === release.relativeDir;
174
+ const range = workspace && (stripped === "^" || stripped === "~") ? `${stripped}${release.oldVersion}` : stripped;
175
+ if (!satisfies(release.newVersion, range)) return true;
176
+ if (depType === "peerDependencies") return !options.onlyUpdatePeerDependentsWhenOutOfRange;
177
+ return bumpLevel[release.type] >= bumpLevel[options.updateInternalDependencies];
178
+ };
179
+ const normalizePath = (path) => path.split("/").reduce((parts, part) => {
180
+ if (part === "" || part === ".") return parts;
181
+ if (part === ".." && parts.length > 0 && parts.at(-1) !== "..") return parts.slice(0, -1);
182
+ return [...parts, part];
183
+ }, []).join("/") || ".";
184
+ const capitalize = (text) => text.charAt(0).toUpperCase() + text.slice(1);
185
+ /** `generateMarkdownForVersionType`: joins release lines, keeping at most one blank line between them. */
186
+ const markdownForType = (type, lines) => {
187
+ const releaseLines = lines.filter((line) => line);
188
+ if (releaseLines.length === 0) return void 0;
189
+ return releaseLines.reduce(({ content, newLines }, line) => {
190
+ const leading = newLines + (/^\n*/.exec(line)?.[0].length ?? 0);
191
+ return {
192
+ content: content + "\n".repeat(Math.min(Math.max(leading, 1), 2)) + line.trim(),
193
+ newLines: /\n*$/.exec(line)?.[0].length ?? 0
194
+ };
195
+ }, {
196
+ content: `### ${capitalize(type)} Changes`,
197
+ newLines: 2
198
+ }).content;
199
+ };
200
+ /** `getChangelogEntry` for one release; `undefined` for a release that does not bump. */
201
+ const changelogEntry = Effect.fn("changelogEntry")(function* (release, releases, changesets, functions, functionOptions, options) {
202
+ if (release.type === "none") return void 0;
203
+ const linesOf = (type) => Effect.forEach(changesets.flatMap((changeset) => {
204
+ const own = changeset.releases.find((r) => r.name === release.name);
205
+ return own !== void 0 && own.type === type ? [changeset] : [];
206
+ }), (changeset) => functions.getReleaseLine(changeset, type, functionOptions));
207
+ const dependentReleases = releases.filter((rel) => {
208
+ const dependencyRange = release.packageJson.dependencies?.[rel.name];
209
+ const peerRange = release.packageJson.peerDependencies?.[rel.name];
210
+ const versionRange = dependencyRange || peerRange;
211
+ return versionRange !== void 0 && versionRange !== "" && (versionRange.startsWith("workspace:") || isValidRange(versionRange)) && shouldUpdateDependency(rel, versionRange, dependencyRange ? "dependencies" : "peerDependencies", options);
212
+ });
213
+ const relevantIds = new Set(dependentReleases.flatMap((rel) => rel.changesets));
214
+ const major = yield* linesOf("major");
215
+ const minor = yield* linesOf("minor");
216
+ const patch = yield* linesOf("patch");
217
+ const dependencyLine = yield* functions.getDependencyReleaseLine(changesets.filter((changeset) => relevantIds.has(changeset.id)), dependentReleases, functionOptions);
218
+ const rendered = [
219
+ `## ${release.newVersion}`,
220
+ markdownForType("major", major),
221
+ markdownForType("minor", minor),
222
+ markdownForType("patch", [...patch, dependencyLine])
223
+ ].filter((line) => line !== void 0 && line !== "");
224
+ return (rendered.length === 1 ? [...rendered, "No changes in this release."] : rendered).join("\n\n");
225
+ });
226
+ /** `updateChangelog`: the new entry goes above the first version heading, or under the title. */
227
+ const prependEntry = (existing, entry, packageName) => {
228
+ const template = `\n\n${entry.trim()}\n`;
229
+ if (existing === void 0 || existing === "") return `# ${packageName}${template}`;
230
+ const firstVersionHeading = existing.search(/^#{1,6}\s+\d+\.\d+/m);
231
+ if (firstVersionHeading >= 0) return existing.slice(0, firstVersionHeading) + template.trimStart() + "\n" + existing.slice(firstVersionHeading);
232
+ const newline = existing.indexOf("\n");
233
+ return newline === -1 ? existing + template : existing.slice(0, newline) + template + existing.slice(newline + 1);
234
+ };
235
+ // src/types.ts
236
+ const DEPENDENCY_TYPES = [
237
+ "dependencies",
238
+ "devDependencies",
239
+ "peerDependencies",
240
+ "optionalDependencies"
241
+ ];
242
+ // src/dependents.ts
243
+ /** `@changesets/get-dependents-graph`: which workspace packages depend on which. */
244
+ const allDependencies = (json, ignoreDevDependencies) => DEPENDENCY_TYPES.reduce((all, type) => {
245
+ Object.entries(json[type] ?? {}).forEach(([name, range]) => {
246
+ if (type === "devDependencies" && (ignoreDevDependencies || range.startsWith("link:") || range.startsWith("file:"))) return;
247
+ all.set(name, range);
248
+ });
249
+ return all;
250
+ }, /* @__PURE__ */ new Map());
251
+ const isProtocolRange = (range) => range.includes(":");
252
+ const isUsableRange = (range) => !isProtocolRange(range) && isValidRange(range);
253
+ const normalizePosix = (path) => path.split("/").reduce((parts, part) => {
254
+ if (part === "" || part === ".") return parts;
255
+ if (part === ".." && parts.length > 0 && parts.at(-1) !== "..") return parts.slice(0, -1);
256
+ return [...parts, part];
257
+ }, []).join("/") || ".";
258
+ const relativeDirOf = (rootDir, pkg) => pkg.dir === rootDir ? "." : pkg.dir.slice(rootDir.length + 1).replaceAll("\\", "/");
259
+ const workspacePathMatches = (range, rootDir, pkg) => normalizePosix(range) === relativeDirOf(rootDir, pkg);
260
+ const dependenciesOf = (pkg, byName, rootDir, options, invalid) => [...allDependencies(pkg.packageJson, options.ignoreDevDependencies ?? false)].flatMap(([name, rawRange]) => {
261
+ const match = byName.get(name);
262
+ if (match === void 0) return [];
263
+ const expected = match.packageJson.version;
264
+ const report = () => {
265
+ invalid.push(`Package ${pkg.packageJson.name} must depend on the current version of ${name}: ${expected} vs ${rawRange}`);
266
+ return [];
267
+ };
268
+ if (rawRange.startsWith("workspace:")) {
269
+ const range = rawRange.slice(10);
270
+ if (range === "*" || range === "^" || range === "~") return [name];
271
+ if (workspacePathMatches(range, rootDir, match)) return [name];
272
+ if (!isUsableRange(range)) return report();
273
+ return satisfies(expected, range) ? [name] : report();
274
+ }
275
+ if (options.bumpVersionsWithWorkspaceProtocolOnly === true) return [];
276
+ if (isProtocolRange(rawRange)) return report();
277
+ if (!isUsableRange(rawRange)) return [];
278
+ return satisfies(expected, rawRange) ? [name] : report();
279
+ });
280
+ const dependentsGraph = (packages, options = {}) => {
281
+ const byName = new Map([[packages.rootPackage.packageJson.name, packages.rootPackage], ...packages.packages.map((pkg) => [pkg.packageJson.name, pkg])]);
282
+ const invalid = [];
283
+ const graph = new Map([[packages.rootPackage.packageJson.name, []], ...packages.packages.map((pkg) => [pkg.packageJson.name, []])]);
284
+ dependenciesOf(packages.rootPackage, byName, packages.rootDir, options, invalid);
285
+ packages.packages.forEach((pkg) => {
286
+ dependenciesOf(pkg, byName, packages.rootDir, options, invalid).forEach((dependency) => graph.get(dependency)?.push(pkg.packageJson.name));
287
+ });
288
+ return {
289
+ graph,
290
+ invalid
291
+ };
292
+ };
293
+ // src/config.ts
294
+ /** `.changeset/config.json`: the written schema, its defaults, and the `@changesets/config` rules. */
295
+ const ModuleTuple = Schema.Tuple([Schema.String, Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown))]);
296
+ const WrittenConfig = Schema.Struct({
297
+ baseBranch: Schema.optionalKey(Schema.String),
298
+ access: Schema.optionalKey(Schema.Literals(["public", "restricted"])),
299
+ changedFilePatterns: Schema.optionalKey(Schema.Array(Schema.String)),
300
+ format: Schema.optionalKey(Schema.Literals([
301
+ "auto",
302
+ "prettier",
303
+ "oxfmt",
304
+ "deno",
305
+ "dprint",
306
+ false
307
+ ])),
308
+ ignore: Schema.optionalKey(Schema.Array(Schema.String)),
309
+ fixed: Schema.optionalKey(Schema.Array(Schema.Array(Schema.String))),
310
+ linked: Schema.optionalKey(Schema.Array(Schema.Array(Schema.String))),
311
+ updateInternalDependencies: Schema.optionalKey(Schema.Literals(["minor", "patch"])),
312
+ privatePackages: Schema.optionalKey(Schema.Union([Schema.Struct({
313
+ version: Schema.optionalKey(Schema.Boolean),
314
+ tag: Schema.optionalKey(Schema.Boolean)
315
+ }), Schema.Boolean])),
316
+ bumpVersionsWithWorkspaceProtocolOnly: Schema.optionalKey(Schema.Boolean),
317
+ snapshot: Schema.optionalKey(Schema.Struct({
318
+ useCalculatedVersion: Schema.optionalKey(Schema.Boolean),
319
+ prereleaseTemplate: Schema.optionalKey(Schema.String)
320
+ })),
321
+ changelog: Schema.optionalKey(Schema.Union([
322
+ Schema.Literal(false),
323
+ Schema.String,
324
+ ModuleTuple
325
+ ])),
326
+ commit: Schema.optionalKey(Schema.Union([
327
+ Schema.Boolean,
328
+ Schema.String,
329
+ ModuleTuple
330
+ ])),
331
+ ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: Schema.optionalKey(Schema.Struct({
332
+ onlyUpdatePeerDependentsWhenOutOfRange: Schema.optionalKey(Schema.Boolean),
333
+ updateInternalDependents: Schema.optionalKey(Schema.Literals(["always", "out-of-range"]))
334
+ }))
335
+ });
336
+ /** A picomatch-style matcher: a `!pattern` matches what `pattern` does not. */
337
+ const matcher = (pattern) => {
338
+ const negated = pattern.startsWith("!");
339
+ const glob = Glob.make(negated ? pattern.slice(1) : pattern);
340
+ return {
341
+ negated,
342
+ test: (value) => negated !== Glob.match(glob, value)
343
+ };
344
+ };
345
+ /** changesets' glob reduction: a positive pattern admits, a later `!pattern` it matches takes back. */
346
+ const matchesPatterns = (value, patterns) => patterns.map(matcher).reduce((passed, m) => passed ? !(m.negated && !m.test(value)) : !m.negated && m.test(value), false);
347
+ const globMatch = (names, patterns) => names.filter((name) => matchesPatterns(name, patterns));
348
+ const moduleReference = (value, fallback) => {
349
+ if (value === void 0) return fallback;
350
+ if (typeof value === "string") return [value, null];
351
+ if (value === true) return ["@changesets/cli/commit", { skipCI: "version" }];
352
+ return value;
353
+ };
354
+ /** `normalizeWrittenConfig`: fills defaults and expands package-name globs. */
355
+ const normalizeConfig = (written, packageNames) => ({
356
+ baseBranch: written.baseBranch ?? "main",
357
+ access: written.access ?? "restricted",
358
+ changedFilePatterns: written.changedFilePatterns ?? ["**"],
359
+ format: written.format ?? "auto",
360
+ ignore: globMatch(packageNames, written.ignore ?? []),
361
+ fixed: (written.fixed ?? []).map((group) => globMatch(packageNames, group)),
362
+ linked: (written.linked ?? []).map((group) => globMatch(packageNames, group)),
363
+ updateInternalDependencies: written.updateInternalDependencies ?? "patch",
364
+ privatePackages: typeof written.privatePackages === "boolean" ? {
365
+ version: written.privatePackages,
366
+ tag: written.privatePackages
367
+ } : {
368
+ version: written.privatePackages?.version ?? false,
369
+ tag: written.privatePackages?.tag ?? false
370
+ },
371
+ bumpVersionsWithWorkspaceProtocolOnly: written.bumpVersionsWithWorkspaceProtocolOnly ?? false,
372
+ snapshot: {
373
+ useCalculatedVersion: written.snapshot?.useCalculatedVersion ?? false,
374
+ prereleaseTemplate: written.snapshot?.prereleaseTemplate
375
+ },
376
+ changelog: moduleReference(written.changelog, ["@changesets/cli/changelog", null]),
377
+ commit: moduleReference(written.commit, false),
378
+ onlyUpdatePeerDependentsWhenOutOfRange: written.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH?.onlyUpdatePeerDependentsWhenOutOfRange ?? false,
379
+ updateInternalDependents: written.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH?.updateInternalDependents ?? "out-of-range"
380
+ });
381
+ /** `shouldSkipPackage`: ignored, private without `privatePackages.version`, or versionless. */
382
+ const shouldSkipPackage = ({ packageJson }, { ignore, allowPrivatePackages }) => ignore.includes(packageJson.name) || packageJson.private === true && !allowPrivatePackages || !packageJson.version;
383
+ const skipOptions = (config) => ({
384
+ ignore: config.ignore,
385
+ allowPrivatePackages: config.privatePackages.version
386
+ });
387
+ const picomatchNote = `Note that glob expressions must be defined according to https://npmx.dev/picomatch.`;
388
+ const unmatched = (patterns, names) => patterns.filter((pattern) => !names.some((name) => matcher(pattern).test(name)));
389
+ const duplicates = (groups) => {
390
+ const flat = groups.flat();
391
+ return [...new Set(flat.filter((name, index) => flat.indexOf(name) !== index))];
392
+ };
393
+ /** The `@changesets/config` validation rules, in the same order and wording. */
394
+ const validateConfig = (config, written, packages) => {
395
+ const names = packages.packages.map((pkg) => pkg.packageJson.name);
396
+ const groupWarnings = (kind, groups) => config[kind].length === 0 ? [] : (groups ?? []).flatMap((group) => unmatched(group, names).map((pattern) => `${kind}: Invalid path: The package or glob "${pattern}" does not match any package in the project. ${picomatchNote}`));
397
+ const groupErrors = (kind) => duplicates(config[kind]).map((name) => `${kind}: Invalid group: The package or glob "${name}" is defined in multiple groups of ${kind} packages. Packages can only be belong to a single group. ${picomatchNote}`);
398
+ const linkedNames = new Set(config.linked.flat());
399
+ const fixedAndLinked = config.fixed.length === 0 || config.linked.length === 0 ? [] : [...new Set(config.fixed.flat())].filter((name) => linkedNames.has(name)).map((name) => `Invalid group: The package "${name}" can be found in both fixed and linked groups. A package can only be either fixed or linked.`);
400
+ const skipped = config.ignore.length === 0 && config.privatePackages.version ? [] : skippedDependentErrors(packages, config).map(({ dependent, skipped }) => `Invalid tree: "${dependent}" depends on the skipped package "${skipped}", but "${dependent}" is not skipped. Please add "${dependent}" to the "ignore" option.`);
401
+ const privateTag = config.privatePackages.version === false && config.privatePackages.tag === true ? [`privatePackages: Invalid combination: "tag" is set to "true" but "version" is set to "false".`] : [];
402
+ return {
403
+ warnings: [...groupWarnings("fixed", written.fixed), ...groupWarnings("linked", written.linked)],
404
+ errors: [
405
+ ...groupErrors("fixed"),
406
+ ...groupErrors("linked"),
407
+ ...fixedAndLinked,
408
+ ...skipped,
409
+ ...privateTag
410
+ ]
411
+ };
412
+ };
413
+ /** Public packages that depend (outside devDependencies) on a package changesets skips. */
414
+ const skippedDependentErrors = (packages, config) => {
415
+ const graph = dependentsGraph(packages, {
416
+ ignoreDevDependencies: true,
417
+ bumpVersionsWithWorkspaceProtocolOnly: config.bumpVersionsWithWorkspaceProtocolOnly
418
+ }).graph;
419
+ const byName = new Map(packages.packages.map((pkg) => [pkg.packageJson.name, pkg]));
420
+ return packages.packages.filter((pkg) => shouldSkipPackage(pkg, skipOptions(config))).flatMap((pkg) => (graph.get(pkg.packageJson.name) ?? []).flatMap((dependent) => {
421
+ const dependentPkg = byName.get(dependent);
422
+ return dependentPkg === void 0 || shouldSkipPackage(dependentPkg, skipOptions(config)) || dependentPkg.packageJson.private === true ? [] : [{
423
+ dependent,
424
+ skipped: pkg.packageJson.name
425
+ }];
426
+ }));
427
+ };
428
+ // src/dependency-edits.ts
429
+ /** Which internal dependency ranges `changeset version` rewrites, and to what. */
430
+ const rangeType = (range) => {
431
+ if (range.startsWith("^")) return "^";
432
+ if (range.startsWith("~")) return "~";
433
+ if (range.startsWith(">=")) return ">=";
434
+ if (range.startsWith("<=")) return "<=";
435
+ if (range.startsWith(">")) return ">";
436
+ return "";
437
+ };
438
+ /** `getNewDependencyRange`: keep the range's operator, or move both bounds of `>=a <b`. */
439
+ const newDependencyRange = (range, newVersion, type) => {
440
+ const sets = rangeComparators(range);
441
+ const comparators = sets.length === 1 ? sets[0] ?? [] : [];
442
+ if (comparators.length === 2 && range.includes(">") && range.includes("<")) {
443
+ const lower = comparators.find(({ operator }) => operator.startsWith(">"));
444
+ const upper = comparators.find(({ operator }) => operator.startsWith("<"));
445
+ if (lower !== void 0 && upper !== void 0) {
446
+ const newUpper = satisfies(newVersion, upper.value) ? upper.value : `<${increment(newVersion, type)}`;
447
+ return comparators[0] === lower ? `>=${newVersion} ${newUpper}` : `${newUpper} >=${newVersion}`;
448
+ }
449
+ }
450
+ return `${rangeType(range)}${newVersion}`;
451
+ };
452
+ /** `getDependencyVersionEdits` for one manifest against every release in the plan. */
453
+ const dependencyEdits = (packageJson, releases, options) => DEPENDENCY_TYPES.flatMap((depType) => {
454
+ const deps = packageJson[depType];
455
+ if (deps === void 0) return [];
456
+ return releases.flatMap((release) => {
457
+ const { name, newVersion, type } = release;
458
+ if (newVersion === void 0 || type === "none") return [];
459
+ const current = deps[name];
460
+ if (!current || current.startsWith("file:") || current.startsWith("link:") || !shouldUpdateDependency(release, current, depType, options)) return [];
461
+ const workspace = current.startsWith("workspace:");
462
+ if (!workspace && (options.bumpVersionsWithWorkspaceProtocolOnly || !isValidRange(current))) return [];
463
+ const range = workspace ? current.slice(10) : current;
464
+ if (workspace && (range === "*" || range === "^" || range === "~" || !isValidRange(range))) return [];
465
+ if (isAnyRange(range) && !isPrerelease(newVersion)) return [];
466
+ const next = newDependencyRange(range, newVersion, type);
467
+ return [{
468
+ keys: [depType, name],
469
+ value: workspace ? `workspace:${next}` : next
470
+ }];
471
+ });
472
+ });
473
+ // src/errors.ts
474
+ /** A `.changeset/*.md` file that changesets itself would also reject. */
475
+ var ChangesetParseError = class extends Schema.TaggedError()("@maple-dev/changesets/ChangesetParseError", {
476
+ message: Schema.String,
477
+ file: Schema.String
478
+ }) {};
479
+ /** `.changeset/config.json` is missing, malformed, or breaks one of the config rules. */
480
+ var ConfigError = class extends Schema.TaggedError()("@maple-dev/changesets/ConfigError", {
481
+ message: Schema.String,
482
+ problems: Schema.Array(Schema.String)
483
+ }) {};
484
+ /** The workspace root or one of its package manifests could not be read. */
485
+ var WorkspaceError = class extends Schema.TaggedError()("@maple-dev/changesets/WorkspaceError", {
486
+ message: Schema.String,
487
+ path: Schema.String,
488
+ cause: Schema.optionalKey(Schema.Defect())
489
+ }) {};
490
+ /** The changesets and the workspace disagree, e.g. a changeset names an unknown package. */
491
+ var ReleasePlanError = class extends Schema.TaggedError()("@maple-dev/changesets/ReleasePlanError", { message: Schema.String }) {};
492
+ var GitError = class extends Schema.TaggedError()("@maple-dev/changesets/GitError", {
493
+ message: Schema.String,
494
+ args: Schema.Array(Schema.String),
495
+ stderr: Schema.String
496
+ }) {};
497
+ var FormatterError = class extends Schema.TaggedError()("@maple-dev/changesets/FormatterError", {
498
+ message: Schema.String,
499
+ formatter: Schema.String,
500
+ stderr: Schema.String
501
+ }) {};
502
+ var JsonEditError = class extends Schema.TaggedError()("@maple-dev/changesets/JsonEditError", {
503
+ message: Schema.String,
504
+ file: Schema.String
505
+ }) {};
506
+ var ChangelogError = class extends Schema.TaggedError()("@maple-dev/changesets/ChangelogError", {
507
+ message: Schema.String,
508
+ module: Schema.String,
509
+ cause: Schema.optionalKey(Schema.Defect())
510
+ }) {};
511
+ /** A changesets feature this tool deliberately does not implement (pre mode, snapshots, commit). */
512
+ var UnsupportedFeatureError = class extends Schema.TaggedError()("@maple-dev/changesets/UnsupportedFeatureError", {
513
+ message: Schema.String,
514
+ feature: Schema.String
515
+ }) {};
516
+ /** An expected non-zero exit that changesets reports the same way, e.g. "No unreleased changesets found." */
517
+ var CommandExit = class extends Schema.TaggedError()("@maple-dev/changesets/CommandExit", {
518
+ message: Schema.String,
519
+ code: Schema.Number
520
+ }) {};
521
+ // src/formatter.ts
522
+ /**
523
+ * `@changesets/format`: with `format: "auto"`, changesets formats the CHANGELOG.md files it
524
+ * wrote (and new changeset files) with the project's formatter, run through the package manager.
525
+ * Detection walks up from the root: config files first, then an installed formatter package.
526
+ */
527
+ const formatters = {
528
+ dprint: {
529
+ packageName: "dprint",
530
+ configFiles: [
531
+ "dprint.json",
532
+ "dprint.jsonc",
533
+ ".dprint.json",
534
+ ".dprint.jsonc"
535
+ ],
536
+ args: (files) => [
537
+ "dprint",
538
+ "fmt",
539
+ "--allow-no-files",
540
+ ...files
541
+ ]
542
+ },
543
+ deno: {
544
+ packageName: void 0,
545
+ configFiles: [
546
+ "deno.json",
547
+ "deno.jsonc",
548
+ {
549
+ file: "deno.json",
550
+ key: "fmt"
551
+ }
552
+ ],
553
+ args: (files) => [
554
+ "fmt",
555
+ "--permit-no-files",
556
+ ...files
557
+ ]
558
+ },
559
+ oxfmt: {
560
+ packageName: "oxfmt",
561
+ configFiles: [
562
+ ".oxfmtrc.json",
563
+ ".oxfmtrc.jsonc",
564
+ "oxfmt.config.ts",
565
+ "oxfmt.config.mts"
566
+ ],
567
+ args: (files) => [
568
+ "oxfmt",
569
+ "--write",
570
+ "--no-error-on-unmatched-pattern",
571
+ ...files
572
+ ]
573
+ },
574
+ prettier: {
575
+ packageName: "prettier",
576
+ configFiles: [
577
+ ".prettierrc",
578
+ ...[
579
+ "json",
580
+ "yml",
581
+ "yaml",
582
+ "json5",
583
+ "js",
584
+ "ts",
585
+ "mjs",
586
+ "mts",
587
+ "cjs",
588
+ "cts",
589
+ "toml"
590
+ ].map((ext) => `.prettierrc.${ext}`),
591
+ ...[
592
+ "js",
593
+ "ts",
594
+ "mjs",
595
+ "mts",
596
+ "cjs",
597
+ "cts"
598
+ ].map((ext) => `prettier.config.${ext}`),
599
+ {
600
+ file: "package.json",
601
+ key: "prettier"
602
+ }
603
+ ],
604
+ args: (files) => [
605
+ "prettier",
606
+ "--write",
607
+ "--no-error-on-unmatched-pattern",
608
+ ...files
609
+ ]
610
+ }
611
+ };
612
+ const detectOrder = [
613
+ "dprint",
614
+ "deno",
615
+ "oxfmt",
616
+ "prettier"
617
+ ];
618
+ const locks = [
619
+ ["bun.lock", "bun"],
620
+ ["bun.lockb", "bun"],
621
+ ["deno.lock", "deno"],
622
+ ["pnpm-lock.yaml", "pnpm"],
623
+ ["pnpm-workspace.yaml", "pnpm"],
624
+ ["yarn.lock", "yarn"],
625
+ ["package-lock.json", "npm"],
626
+ ["npm-shrinkwrap.json", "npm"]
627
+ ];
628
+ const executeLocal = (agent, args) => {
629
+ switch (agent) {
630
+ case "bun": return ["bun", ["x", ...args]];
631
+ case "pnpm": return ["pnpm", ["exec", ...args]];
632
+ case "yarn": return ["yarn", ["exec", ...args]];
633
+ case "deno": return ["deno", [
634
+ "task",
635
+ "--eval",
636
+ ...args
637
+ ]];
638
+ case "npm": return ["npx", args];
639
+ }
640
+ };
641
+ const PackageManagerField = Schema.Struct({ packageManager: Schema.optionalKey(Schema.String) });
642
+ const decodeKeys = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)));
643
+ const decodePackageManager = Schema.decodeUnknownOption(Schema.fromJsonString(PackageManagerField));
644
+ var Formatter = class extends Context.Service()("@maple-dev/changesets/Formatter", { make: Effect.gen(function* () {
645
+ const fs = yield* FileSystem.FileSystem;
646
+ const path = yield* Path.Path;
647
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
648
+ const exists = (file) => fs.exists(file).pipe(Effect.orElseSucceed(() => false));
649
+ const readText = (file) => fs.readFileString(file).pipe(Effect.option);
650
+ const ancestors = (dir) => {
651
+ const parent = path.dirname(dir);
652
+ return parent === dir ? [dir] : [dir, ...ancestors(parent)];
653
+ };
654
+ const firstSome = (items, f) => {
655
+ const [head, ...rest] = items;
656
+ if (head === void 0) return Effect.succeed(Option.none());
657
+ return Effect.flatMap(f(head), (found) => Option.isSome(found) ? Effect.succeed(found) : firstSome(rest, f));
658
+ };
659
+ const hasConfig = (dir, configFile) => typeof configFile === "string" ? exists(path.join(dir, configFile)) : Effect.map(readText(path.join(dir, configFile.file)), (text) => Option.isSome(Option.flatMap(text, decodeKeys).pipe(Option.filter((keys) => configFile.key in keys))));
660
+ const byConfig = (dir) => firstSome(detectOrder, (name) => Effect.map(Effect.forEach(formatters[name].configFiles, (configFile) => hasConfig(dir, configFile)), (found) => found.some(Boolean) ? Option.some(name) : Option.none()));
661
+ const byPackage = (dir) => firstSome(detectOrder, (name) => {
662
+ const packageName = formatters[name].packageName;
663
+ return packageName === void 0 ? Effect.succeed(Option.none()) : Effect.map(exists(path.join(dir, "node_modules", packageName)), (found) => found ? Option.some(name) : Option.none());
664
+ });
665
+ const detect = Effect.fn("Formatter.detect")(function* (format, rootDir) {
666
+ if (format === false) return Option.none();
667
+ if (format !== "auto") return Option.some(format);
668
+ const dirs = ancestors(path.resolve(rootDir));
669
+ const configured = yield* firstSome(dirs, byConfig);
670
+ return Option.isSome(configured) ? configured : yield* firstSome(dirs, byPackage);
671
+ });
672
+ const packageManagerIn = (dir) => Effect.map(readText(path.join(dir, "package.json")), (text) => Option.flatMap(Option.flatMap(text, decodePackageManager), (json) => Option.fromNullishOr(json.packageManager?.split("@")[0]).pipe(Option.filter((agent) => [
673
+ "bun",
674
+ "deno",
675
+ "pnpm",
676
+ "yarn",
677
+ "npm"
678
+ ].includes(agent)))));
679
+ const agentFor = (rootDir) => Effect.map(firstSome(ancestors(path.resolve(rootDir)), (dir) => Effect.gen(function* () {
680
+ const lock = yield* firstSome(locks.map(([file]) => file), (file) => Effect.map(exists(path.join(dir, file)), (found) => found ? Option.some(file) : Option.none()));
681
+ const declared = yield* packageManagerIn(dir);
682
+ if (Option.isSome(lock)) {
683
+ const fromLock = locks.find(([file]) => file === lock.value)?.[1] ?? "npm";
684
+ return Option.some(Option.getOrElse(declared, () => fromLock));
685
+ }
686
+ return declared;
687
+ })), Option.getOrElse(() => "npm"));
688
+ return {
689
+ detect,
690
+ format: Effect.fn("Formatter.format")(function* (config, rootDir, files) {
691
+ const name = yield* detect(config, rootDir);
692
+ if (Option.isNone(name) || files.length === 0) return;
693
+ const spec = formatters[name.value];
694
+ const [command, args] = name.value === "deno" ? ["deno", spec.args(files)] : executeLocal(yield* agentFor(rootDir), spec.args(files));
695
+ const result = yield* Effect.scoped(Effect.gen(function* () {
696
+ const handle = yield* spawner.spawn(ChildProcess.make(command, [...args], { cwd: rootDir }));
697
+ const [, stderr, exitCode] = yield* Effect.all([
698
+ Stream.runDrain(handle.stdout),
699
+ Stream.mkString(Stream.decodeText(handle.stderr)),
700
+ handle.exitCode
701
+ ], { concurrency: "unbounded" });
702
+ return {
703
+ stderr,
704
+ exitCode: Number(exitCode)
705
+ };
706
+ })).pipe(Effect.mapError((cause) => new FormatterError({
707
+ message: `Could not run ${command}: ${cause.message}`,
708
+ formatter: name.value,
709
+ stderr: ""
710
+ })));
711
+ if (result.exitCode !== 0) return yield* new FormatterError({
712
+ message: `${[command, ...args.slice(0, 2)].join(" ")} exited with code ${result.exitCode}`,
713
+ formatter: name.value,
714
+ stderr: result.stderr
715
+ });
716
+ })
717
+ };
718
+ }) }) {
719
+ static layer = Layer.effect(this, this.make);
720
+ };
721
+ // src/frontmatter.ts
722
+ /**
723
+ * `@changesets/parse` without a YAML dependency. The frontmatter grammar covers what changesets
724
+ * accepts in practice: a block or flow mapping of plain, single- or double-quoted package names to
725
+ * release types, with comments and folded values. Anything else is reported as invalid YAML.
726
+ */
727
+ const isSpace = (char) => /^\s$/.test(char);
728
+ /** For each index, the first index at or after it whose character is not whitespace (or `length`). */
729
+ const nextNonSpaceIndex = (text) => Array.from({ length: text.length }, (_, i) => i).reduceRight((next, i) => {
730
+ next[i] = isSpace(text.charAt(i)) ? next[i + 1] ?? text.length : i;
731
+ return next;
732
+ }, Array(text.length + 1).fill(text.length));
733
+ /**
734
+ * `@changesets/parse`'s `/\s*---([^]*?)\r?\n\s*---(\s*(?:\n|$)[^]*)/` as one linear scan.
735
+ * Only the first `---` can open a match: a later one has a subset of the same closing lines.
736
+ * The closing line is the first `\n` (or `\r\n`) then optional whitespace then `---`, followed
737
+ * by whitespace that holds a newline or runs to the end.
738
+ */
739
+ const splitFrontmatter = (text) => {
740
+ const opener = text.indexOf("---");
741
+ if (opener === -1) return void 0;
742
+ const start = opener + 3;
743
+ const nextNonSpace = nextNonSpaceIndex(text);
744
+ const closes = (e) => {
745
+ const lineStart = text.startsWith("\r\n", e) ? e + 2 : text.charAt(e) === "\n" ? e + 1 : -1;
746
+ if (lineStart === -1) return void 0;
747
+ const dashes = nextNonSpace[lineStart] ?? text.length;
748
+ if (!text.startsWith("---", dashes)) return void 0;
749
+ const after = dashes + 3;
750
+ const end = nextNonSpace[after] ?? text.length;
751
+ const newline = text.indexOf("\n", after);
752
+ return end === text.length || newline !== -1 && newline < end ? after : void 0;
753
+ };
754
+ const close = Array.from({ length: Math.max(text.length - start, 0) }, (_, i) => start + i).find((e) => closes(e) !== void 0);
755
+ if (close === void 0) return void 0;
756
+ return {
757
+ releases: text.slice(start, close),
758
+ summary: text.slice(closes(close))
759
+ };
760
+ };
761
+ const EXAMPLE_FORMAT = `---\n"package-name": patch\n---`;
762
+ const validVersionTypes = [
763
+ "major",
764
+ "minor",
765
+ "patch",
766
+ "none"
767
+ ];
768
+ const truncate = (s, max = 200) => s.length > max ? s.slice(0, max) + "..." : s;
769
+ const resolvePlain = (text) => {
770
+ if (/^(~|null|Null|NULL)?$/.test(text)) return null;
771
+ if (/^(true|True|TRUE)$/.test(text)) return true;
772
+ if (/^(false|False|FALSE)$/.test(text)) return false;
773
+ if (/^[-+]?[0-9]+$/.test(text)) return Number.parseInt(text, 10);
774
+ if (/^0o[0-7]+$/.test(text)) return Number.parseInt(text.slice(2), 8);
775
+ if (/^0x[0-9a-fA-F]+$/.test(text)) return Number.parseInt(text.slice(2), 16);
776
+ if (/^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$/.test(text)) return Number.parseFloat(text);
777
+ if (/^[-+]?\.(inf|Inf|INF)$/.test(text)) return text.startsWith("-") ? -Infinity : Infinity;
778
+ if (/^\.(nan|NaN|NAN)$/.test(text)) return NaN;
779
+ return text;
780
+ };
781
+ const escapes = /* @__PURE__ */ new Map([
782
+ ["0", "\0"],
783
+ ["a", "\x07"],
784
+ ["b", "\b"],
785
+ ["t", " "],
786
+ [" ", " "],
787
+ ["n", "\n"],
788
+ ["v", "\v"],
789
+ ["f", "\f"],
790
+ ["r", "\r"],
791
+ ["e", "\x1B"],
792
+ [" ", " "],
793
+ ["\"", "\""],
794
+ ["/", "/"],
795
+ ["\\", "\\"],
796
+ ["N", "Ā…"],
797
+ ["_", "\xA0"],
798
+ ["L", "\u2028"],
799
+ ["P", "\u2029"]
800
+ ]);
801
+ /** Reads a quoted scalar starting at `text[0]`; returns the value and the rest of the line. */
802
+ const readQuoted = (text) => {
803
+ const quote = text[0];
804
+ const chars = Array.from(text.slice(1));
805
+ const read = (index, value) => {
806
+ const char = chars[index];
807
+ if (char === void 0) return Result.fail("Missing closing quote");
808
+ if (quote === "'") {
809
+ if (char !== "'") return read(index + 1, value + char);
810
+ return chars[index + 1] === "'" ? read(index + 2, value + "'") : Result.succeed({
811
+ value,
812
+ rest: chars.slice(index + 1).join("")
813
+ });
814
+ }
815
+ if (char === "\"") return Result.succeed({
816
+ value,
817
+ rest: chars.slice(index + 1).join("")
818
+ });
819
+ if (char !== "\\") return read(index + 1, value + char);
820
+ const escape = chars[index + 1] ?? "";
821
+ const hexLength = escape === "x" ? 2 : escape === "u" ? 4 : escape === "U" ? 8 : 0;
822
+ if (hexLength > 0) {
823
+ const hex = chars.slice(index + 2, index + 2 + hexLength).join("");
824
+ return new RegExp(`^[0-9a-fA-F]{${hexLength}}$`).test(hex) ? read(index + 2 + hexLength, value + String.fromCodePoint(Number.parseInt(hex, 16))) : Result.fail("Invalid escape sequence");
825
+ }
826
+ const replacement = escapes.get(escape);
827
+ return replacement === void 0 ? Result.fail(`Invalid escape sequence \\${escape}`) : read(index + 2, value + replacement);
828
+ };
829
+ return read(0, "");
830
+ };
831
+ const stripComment = (text) => {
832
+ const at = text.search(/(?:^|\s)#/);
833
+ return (at === -1 ? text : text.slice(0, at)).trim();
834
+ };
835
+ const isQuote = (text) => text.startsWith("\"") || text.startsWith("'");
836
+ const readValue = (raw) => {
837
+ const text = raw.trim();
838
+ if (isQuote(text)) return Result.flatMap(readQuoted(text), ({ value, rest }) => stripComment(rest) === "" ? Result.succeed(value) : Result.fail("Unexpected content after a quoted scalar"));
839
+ if (text.startsWith("[") || text.startsWith("{")) return Result.succeed("object");
840
+ if (/^[&*!|>%@`]/.test(text)) return Result.fail(`Unsupported YAML syntax: ${truncate(text, 40)}`);
841
+ const plain = stripComment(text);
842
+ return /:(\s|$)/.test(plain) ? Result.fail("Nested mappings are not allowed in compact mappings") : Result.succeed(resolvePlain(plain));
843
+ };
844
+ /** Reads `key:` at the start of `line`; `undefined` when the line is not a mapping entry. */
845
+ const readKey = (line) => {
846
+ if (isQuote(line)) return Result.map(readQuoted(line), ({ value, rest }) => {
847
+ const colon = /^\s*:(\s|$)/.exec(rest);
848
+ return colon === null ? void 0 : {
849
+ key: value,
850
+ rest: rest.slice(colon[0].length)
851
+ };
852
+ });
853
+ const colon = /:(\s|$)/.exec(line);
854
+ if (colon === null) return Result.succeed(void 0);
855
+ const key = line.slice(0, colon.index).trim();
856
+ if (/^[&*!|>%@`[\]{},#]/.test(key) || /^[-?:]\s/.test(key)) return Result.fail(`Plain value cannot start with reserved character ${key[0]}`);
857
+ const resolved = resolvePlain(key);
858
+ return Result.succeed({
859
+ key: resolved === null ? "" : String(resolved),
860
+ rest: line.slice(colon.index + 1)
861
+ });
862
+ };
863
+ const readFlowValue = (text) => {
864
+ if (isQuote(text)) return Result.map(readQuoted(text), ({ value, rest }) => ({
865
+ value,
866
+ after: rest
867
+ }));
868
+ const comma = text.indexOf(",");
869
+ const end = comma === -1 ? text.length : comma;
870
+ return Result.succeed({
871
+ value: resolvePlain(text.slice(0, end).trim()),
872
+ after: text.slice(end)
873
+ });
874
+ };
875
+ const parseFlowEntries = (rest, entries) => Result.gen(function* () {
876
+ const trimmed = rest.trimStart();
877
+ if (trimmed === "") return entries;
878
+ const keyed = yield* readKey(trimmed.replace(/^([^"',]*?):(?=[^\s])/, "$1: "));
879
+ if (keyed === void 0) return yield* Result.fail("Expected a key in the flow mapping");
880
+ const { value, after } = yield* readFlowValue(keyed.rest.trimStart());
881
+ const tail = after.trimStart();
882
+ if (tail !== "" && !tail.startsWith(",")) return yield* Result.fail("Expected , in the flow mapping");
883
+ return yield* parseFlowEntries(tail.slice(1), [...entries, {
884
+ key: keyed.key,
885
+ value
886
+ }]);
887
+ });
888
+ const parseFlowMapping = (text) => {
889
+ const body = text.trim();
890
+ return body.endsWith("}") ? parseFlowEntries(body.slice(1, -1), []) : Result.fail("Unterminated flow mapping");
891
+ };
892
+ const indentOf = (line) => /^[ \t]*/.exec(line)?.[0].length ?? 0;
893
+ const readNestedValue = (nested) => Result.gen(function* () {
894
+ const first = nested[0]?.trim() ?? "";
895
+ if (first.startsWith("- ") || (yield* readKey(first)) !== void 0) return "object";
896
+ return yield* readValue(nested.map((line) => line.trim()).join(" "));
897
+ });
898
+ const parseBlockMapping = (lines) => {
899
+ const baseIndent = indentOf(lines[0] ?? "");
900
+ const step = (index, entries) => Result.gen(function* () {
901
+ const line = lines[index];
902
+ if (line === void 0) return entries;
903
+ if (indentOf(line) !== baseIndent) return yield* Result.fail("Bad indentation of a mapping entry");
904
+ const keyed = yield* readKey(line.trim());
905
+ if (keyed === void 0) return yield* Result.fail("Implicit map keys need to be followed by map values");
906
+ const end = lines.slice(index + 1).findIndex((next) => indentOf(next) <= baseIndent);
907
+ const nested = lines.slice(index + 1, end === -1 ? lines.length : index + 1 + end);
908
+ const inline = stripComment(keyed.rest) === "" ? "" : keyed.rest;
909
+ if (inline !== "" && nested.length > 0) return yield* Result.fail("Bad indentation of a mapping entry");
910
+ const value = nested.length === 0 ? yield* readValue(inline) : yield* readNestedValue(nested);
911
+ return yield* step(index + 1 + nested.length, [...entries, {
912
+ key: keyed.key,
913
+ value
914
+ }]);
915
+ });
916
+ return step(0, []);
917
+ };
918
+ const parseDocument$1 = (text) => Result.gen(function* () {
919
+ const lines = text.split(/\r?\n/).map((line) => line.replace(/\r$/, "")).filter((line) => line.trim() !== "" && !line.trim().startsWith("#"));
920
+ const first = lines[0]?.trim();
921
+ if (first === void 0) return { _tag: "Empty" };
922
+ if (first.startsWith("{")) return {
923
+ _tag: "Map",
924
+ entries: yield* parseFlowMapping(lines.map(stripComment).join(" "))
925
+ };
926
+ if (first.startsWith("- ") || first === "-" || first.startsWith("[")) return { _tag: "Other" };
927
+ if (lines.length === 1 && (yield* readKey(first)) === void 0) {
928
+ yield* readValue(first);
929
+ return { _tag: "Other" };
930
+ }
931
+ return {
932
+ _tag: "Map",
933
+ entries: yield* parseBlockMapping(lines)
934
+ };
935
+ });
936
+ const typeName = (value) => value === null || value === "object" ? "object" : typeof value;
937
+ const validateReleases = (entries, contents) => {
938
+ const [first] = entries.flatMap((entry) => {
939
+ if (entry.key.trim() === "") return [`could not parse changeset - invalid package name in frontmatter.\nExpected a non-empty string for package name, but got: ${JSON.stringify(entry.key)}\nChangeset contents:\n${truncate(contents)}`];
940
+ if (typeof entry.value !== "string" || entry.value === "object") return [`could not parse changeset - invalid release type for package "${entry.key}".\nExpected a string for release type, but got: ${typeName(entry.value)}\nChangeset contents:\n${truncate(contents)}`];
941
+ if (!validVersionTypes.includes(entry.value)) return [`could not parse changeset - invalid version type ${JSON.stringify(entry.value)} for package "${entry.key}".\nValid version types are: ${validVersionTypes.join(", ")}\nChangeset contents:\n${truncate(contents)}`];
942
+ return [];
943
+ });
944
+ if (first !== void 0) return Result.fail(first);
945
+ return Result.succeed(entries.flatMap((entry) => entry.value === "major" || entry.value === "minor" || entry.value === "patch" || entry.value === "none" ? [{
946
+ name: entry.key,
947
+ type: entry.value
948
+ }] : []));
949
+ };
950
+ const readDocument = (yaml) => Result.flatMap(parseDocument$1(yaml), (document) => {
951
+ if (document._tag !== "Map") return Result.succeed(document);
952
+ const keys = document.entries.map((entry) => entry.key);
953
+ return new Set(keys).size === keys.length ? Result.succeed(document) : Result.fail("Map keys must be unique");
954
+ });
955
+ /** Parses one changeset file; the failure is the message changesets would print. */
956
+ const parseChangesetFile = (contents) => {
957
+ const trimmedContents = contents.trim();
958
+ if (!trimmedContents) return Result.fail(`could not parse changeset - file is empty.\nChangesets must have frontmatter with package names and version types.\nExample:\n${EXAMPLE_FORMAT}\n\nYour changeset summary here.`);
959
+ const split = splitFrontmatter(contents);
960
+ if (split === void 0) return Result.fail(`could not parse changeset - missing or invalid frontmatter.\nChangesets must start with frontmatter delimited by "---".\nExample:\n${EXAMPLE_FORMAT}\n\nYour changeset summary here.\nReceived content:\n${truncate(trimmedContents)}`);
961
+ const { releases: roughReleases, summary: roughSummary } = split;
962
+ const summary = roughSummary.trim();
963
+ const document = readDocument(roughReleases);
964
+ if (Result.isFailure(document)) return Result.fail(`could not parse changeset - invalid YAML in frontmatter.\nThe frontmatter between the "---" delimiters must be valid YAML.\nYAML error: ${document.failure}\nFrontmatter content:\n${roughReleases}`);
965
+ if (document.success._tag === "Other") return Result.fail(`could not parse changeset - frontmatter must be an object mapping package names to version types.\nExpected format:\n${EXAMPLE_FORMAT}\nReceived:\n${roughReleases}`);
966
+ const entries = document.success._tag === "Empty" ? [] : document.success.entries;
967
+ return Result.map(validateReleases(entries, contents), (releases) => ({
968
+ releases,
969
+ summary
970
+ }));
971
+ };
972
+ // src/git.ts
973
+ /** The git queries changesets makes (`@changesets/git`), over `effect/process`. */
974
+ var Git = class extends Context.Service()("@maple-dev/changesets/Git", { make: Effect.gen(function* () {
975
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
976
+ const fs = yield* FileSystem.FileSystem;
977
+ const path = yield* Path.Path;
978
+ const run = (args, cwd) => Effect.scoped(Effect.gen(function* () {
979
+ const handle = yield* spawner.spawn(ChildProcess.make("git", [...args], { cwd }));
980
+ const [stdout, stderr, exitCode] = yield* Effect.all([
981
+ Stream.mkString(Stream.decodeText(handle.stdout)),
982
+ Stream.mkString(Stream.decodeText(handle.stderr)),
983
+ handle.exitCode
984
+ ], { concurrency: "unbounded" });
985
+ return {
986
+ exitCode: Number(exitCode),
987
+ stdout,
988
+ stderr
989
+ };
990
+ })).pipe(Effect.mapError((cause) => new GitError({
991
+ message: `Could not run git: ${cause.message}`,
992
+ args: [...args],
993
+ stderr: ""
994
+ })));
995
+ const runOk = (args, cwd, message) => Effect.flatMap(run(args, cwd), (result) => result.exitCode === 0 ? Effect.succeed(result.stdout) : Effect.fail(new GitError({
996
+ message,
997
+ args: [...args],
998
+ stderr: result.stderr
999
+ })));
1000
+ const isShallow = Effect.fn("Git.isShallow")(function* (cwd) {
1001
+ const output = (yield* run(["rev-parse", "--is-shallow-repository"], cwd)).stdout.trim();
1002
+ if (output !== "--is-shallow-repository") return output === "true";
1003
+ const gitDir = (yield* run(["rev-parse", "--git-dir"], cwd)).stdout.trim();
1004
+ return yield* fs.exists(path.join(path.resolve(cwd, gitDir), "shallow")).pipe(Effect.orElseSucceed(() => false));
1005
+ });
1006
+ const lookup = (paths, cwd, found) => Effect.gen(function* () {
1007
+ const infos = yield* Effect.forEach(paths, (gitPath) => Effect.map(run([
1008
+ "log",
1009
+ "--diff-filter=AC",
1010
+ "--follow",
1011
+ "--max-count=1",
1012
+ "--pretty=format:%H:%p",
1013
+ gitPath
1014
+ ], cwd), ({ stdout }) => {
1015
+ const [commitSha = "", parentSha = ""] = stdout.split(":");
1016
+ return {
1017
+ gitPath,
1018
+ commitSha,
1019
+ parentSha
1020
+ };
1021
+ }), { concurrency: 8 });
1022
+ const resolved = new Map(found);
1023
+ infos.forEach((info) => {
1024
+ if (info.commitSha && info.parentSha) resolved.set(info.gitPath, info.commitSha);
1025
+ });
1026
+ const missingParents = infos.filter((info) => info.commitSha && !info.parentSha);
1027
+ if (missingParents.length === 0) return resolved;
1028
+ if (yield* isShallow(cwd)) {
1029
+ yield* runOk(["fetch", "--deepen=50"], cwd, "Could not deepen the shallow clone");
1030
+ return yield* lookup(missingParents.map((info) => info.gitPath), cwd, resolved);
1031
+ }
1032
+ missingParents.forEach((info) => resolved.set(info.gitPath, info.commitSha));
1033
+ return resolved;
1034
+ });
1035
+ const commitsThatAddFiles = Effect.fn("Git.commitsThatAddFiles")(function* (paths, cwd) {
1036
+ const commits = yield* lookup(paths, cwd, /* @__PURE__ */ new Map());
1037
+ return paths.map((gitPath) => commits.get(gitPath));
1038
+ });
1039
+ const divergedCommit = (ref, cwd) => Effect.map(runOk([
1040
+ "merge-base",
1041
+ ref,
1042
+ "HEAD"
1043
+ ], cwd, `Failed to find where HEAD diverged from "${ref}". Does "${ref}" exist and it's synced with remote?`), (stdout) => stdout.trim());
1044
+ return {
1045
+ run,
1046
+ commitsThatAddFiles,
1047
+ changedFilesSince: Effect.fn("Git.changedFilesSince")(function* (ref, cwd) {
1048
+ const diverged = yield* divergedCommit(ref, cwd);
1049
+ const files = (yield* runOk([
1050
+ "diff",
1051
+ "--name-only",
1052
+ "--no-relative",
1053
+ diverged
1054
+ ], cwd, `Failed to diff against ${diverged}. Is ${diverged} a valid ref?`)).trim().split("\n").filter((file) => file);
1055
+ const cdup = (yield* runOk(["rev-parse", "--show-cdup"], cwd, "Could not find the git root")).trim();
1056
+ const repoRoot = path.resolve(cwd, cdup.replace(/\n|\r/g, ""));
1057
+ return files.map((file) => path.resolve(repoRoot, file));
1058
+ }),
1059
+ changedChangesetFilesSince: Effect.fn("Git.changedChangesetFilesSince")(function* (ref, cwd) {
1060
+ const diverged = yield* divergedCommit(ref, cwd);
1061
+ return (yield* runOk([
1062
+ "diff",
1063
+ "--name-only",
1064
+ "--diff-filter=d",
1065
+ "--no-relative",
1066
+ diverged
1067
+ ], cwd, `Failed to diff against ${diverged}`)).trim().split("\n").filter((file) => /\.changeset\/[^/]+\.md$/.test(file) || /\.changeset\/pre\/[^/]+\.md$/.test(file));
1068
+ })
1069
+ };
1070
+ }) }) {
1071
+ static layer = Layer.effect(this, this.make);
1072
+ };
1073
+ // src/human-id.ts
1074
+ /** `adjective-nouns-verb` file names in the style of `human-id`, which changesets uses. */
1075
+ const adjectives = [
1076
+ "brave",
1077
+ "bright",
1078
+ "calm",
1079
+ "clean",
1080
+ "clever",
1081
+ "cool",
1082
+ "cuddly",
1083
+ "curly",
1084
+ "cute",
1085
+ "dirty",
1086
+ "dry",
1087
+ "eager",
1088
+ "early",
1089
+ "easy",
1090
+ "empty",
1091
+ "fair",
1092
+ "fancy",
1093
+ "fast",
1094
+ "fluffy",
1095
+ "free",
1096
+ "fresh",
1097
+ "friendly",
1098
+ "funny",
1099
+ "fuzzy",
1100
+ "gentle",
1101
+ "giant",
1102
+ "good",
1103
+ "great",
1104
+ "green",
1105
+ "happy",
1106
+ "heavy",
1107
+ "honest",
1108
+ "hot",
1109
+ "huge",
1110
+ "kind",
1111
+ "large",
1112
+ "lazy",
1113
+ "light",
1114
+ "little",
1115
+ "long",
1116
+ "loud",
1117
+ "lovely",
1118
+ "lucky",
1119
+ "mean",
1120
+ "modern",
1121
+ "neat",
1122
+ "nice",
1123
+ "odd",
1124
+ "old",
1125
+ "plenty",
1126
+ "polite",
1127
+ "proud",
1128
+ "purple",
1129
+ "quick",
1130
+ "quiet",
1131
+ "rare",
1132
+ "red",
1133
+ "rich",
1134
+ "rotten",
1135
+ "rude",
1136
+ "shaggy",
1137
+ "sharp",
1138
+ "shiny",
1139
+ "short",
1140
+ "shy",
1141
+ "silent",
1142
+ "silly",
1143
+ "slimy",
1144
+ "slow",
1145
+ "small",
1146
+ "smart",
1147
+ "smooth",
1148
+ "soft",
1149
+ "sour",
1150
+ "spicy",
1151
+ "strong",
1152
+ "sweet",
1153
+ "tall",
1154
+ "tame",
1155
+ "tasty",
1156
+ "thick",
1157
+ "thin",
1158
+ "tidy",
1159
+ "tiny",
1160
+ "tough",
1161
+ "twelve",
1162
+ "violet",
1163
+ "warm",
1164
+ "weak",
1165
+ "wet",
1166
+ "whole",
1167
+ "wicked",
1168
+ "wide",
1169
+ "wild",
1170
+ "wise",
1171
+ "witty",
1172
+ "young"
1173
+ ];
1174
+ const nouns = [
1175
+ "ants",
1176
+ "apes",
1177
+ "apples",
1178
+ "bags",
1179
+ "bananas",
1180
+ "bars",
1181
+ "baths",
1182
+ "beans",
1183
+ "bears",
1184
+ "beds",
1185
+ "bees",
1186
+ "berries",
1187
+ "bikes",
1188
+ "birds",
1189
+ "boats",
1190
+ "bobcats",
1191
+ "books",
1192
+ "boxes",
1193
+ "buckets",
1194
+ "bugs",
1195
+ "buses",
1196
+ "buttons",
1197
+ "cameras",
1198
+ "candles",
1199
+ "carrots",
1200
+ "cars",
1201
+ "cats",
1202
+ "chairs",
1203
+ "cheetahs",
1204
+ "chefs",
1205
+ "cherries",
1206
+ "chicken",
1207
+ "clocks",
1208
+ "clouds",
1209
+ "coats",
1210
+ "coins",
1211
+ "cooks",
1212
+ "cougars",
1213
+ "cows",
1214
+ "crabs",
1215
+ "crews",
1216
+ "cups",
1217
+ "dancers",
1218
+ "deer",
1219
+ "dodos",
1220
+ "dogs",
1221
+ "dolls",
1222
+ "donkeys",
1223
+ "doors",
1224
+ "dots",
1225
+ "dragons",
1226
+ "drinks",
1227
+ "ducks",
1228
+ "eagles",
1229
+ "eels",
1230
+ "eggs",
1231
+ "experts",
1232
+ "falcons",
1233
+ "fans",
1234
+ "feet",
1235
+ "files",
1236
+ "fireants",
1237
+ "fish",
1238
+ "flies",
1239
+ "flowers",
1240
+ "forks",
1241
+ "foxes",
1242
+ "frogs",
1243
+ "garlics",
1244
+ "geckos",
1245
+ "geese",
1246
+ "ghosts",
1247
+ "gifts",
1248
+ "goats",
1249
+ "grapes",
1250
+ "guests",
1251
+ "hairs",
1252
+ "hats",
1253
+ "hornets",
1254
+ "horses",
1255
+ "houses",
1256
+ "icons",
1257
+ "insects",
1258
+ "islands",
1259
+ "jars",
1260
+ "jeans",
1261
+ "jobs",
1262
+ "keys",
1263
+ "kids",
1264
+ "kings",
1265
+ "kiwis",
1266
+ "knives",
1267
+ "ladybugs",
1268
+ "lamps",
1269
+ "laws",
1270
+ "lemons",
1271
+ "lies",
1272
+ "lights",
1273
+ "lions",
1274
+ "lizards",
1275
+ "llamas",
1276
+ "maps",
1277
+ "masks",
1278
+ "meals",
1279
+ "melons",
1280
+ "mice",
1281
+ "mirrors",
1282
+ "moles",
1283
+ "monkeys",
1284
+ "moons",
1285
+ "moose",
1286
+ "mugs",
1287
+ "mules",
1288
+ "nails",
1289
+ "news",
1290
+ "nights",
1291
+ "numbers",
1292
+ "olives",
1293
+ "onions",
1294
+ "oranges",
1295
+ "otters",
1296
+ "owls",
1297
+ "pandas",
1298
+ "pans",
1299
+ "pants",
1300
+ "papayas",
1301
+ "parents",
1302
+ "parrots",
1303
+ "paths",
1304
+ "peaches",
1305
+ "pears",
1306
+ "peas",
1307
+ "pens",
1308
+ "pets",
1309
+ "phones",
1310
+ "pianos",
1311
+ "pigs",
1312
+ "pillows",
1313
+ "planes",
1314
+ "planets",
1315
+ "plants",
1316
+ "plums",
1317
+ "points",
1318
+ "poets",
1319
+ "pots",
1320
+ "pugs",
1321
+ "queens",
1322
+ "rabbits",
1323
+ "radios",
1324
+ "rats",
1325
+ "ravens",
1326
+ "rice",
1327
+ "rings",
1328
+ "rivers",
1329
+ "rocks",
1330
+ "roses",
1331
+ "rules",
1332
+ "schools",
1333
+ "seals",
1334
+ "seas",
1335
+ "sheep",
1336
+ "shirts",
1337
+ "shoes",
1338
+ "shrimps",
1339
+ "singers",
1340
+ "sloths",
1341
+ "snails",
1342
+ "snakes",
1343
+ "socks",
1344
+ "spiders",
1345
+ "spies",
1346
+ "spoons",
1347
+ "squids",
1348
+ "stamps",
1349
+ "stars",
1350
+ "steaks",
1351
+ "suits",
1352
+ "swans",
1353
+ "tables",
1354
+ "teachers",
1355
+ "teams",
1356
+ "teeth",
1357
+ "terms",
1358
+ "ties",
1359
+ "tigers",
1360
+ "toes",
1361
+ "tools",
1362
+ "towns",
1363
+ "toys",
1364
+ "trains",
1365
+ "trees",
1366
+ "turkeys",
1367
+ "turtles",
1368
+ "vans",
1369
+ "walls",
1370
+ "wasps",
1371
+ "waves",
1372
+ "ways",
1373
+ "weeks",
1374
+ "windows",
1375
+ "wings",
1376
+ "wolves",
1377
+ "worms",
1378
+ "zebras"
1379
+ ];
1380
+ const verbs = [
1381
+ "accept",
1382
+ "add",
1383
+ "admire",
1384
+ "agree",
1385
+ "allow",
1386
+ "appear",
1387
+ "argue",
1388
+ "arrive",
1389
+ "attack",
1390
+ "attend",
1391
+ "bake",
1392
+ "bathe",
1393
+ "battle",
1394
+ "beam",
1395
+ "beg",
1396
+ "begin",
1397
+ "behave",
1398
+ "bet",
1399
+ "boil",
1400
+ "bow",
1401
+ "brake",
1402
+ "breathe",
1403
+ "brush",
1404
+ "build",
1405
+ "burn",
1406
+ "buy",
1407
+ "call",
1408
+ "camp",
1409
+ "care",
1410
+ "carry",
1411
+ "change",
1412
+ "cheat",
1413
+ "check",
1414
+ "cheer",
1415
+ "chew",
1416
+ "clap",
1417
+ "clean",
1418
+ "collect",
1419
+ "compare",
1420
+ "compete",
1421
+ "complain",
1422
+ "confess",
1423
+ "construct",
1424
+ "cough",
1425
+ "count",
1426
+ "cover",
1427
+ "crash",
1428
+ "cross",
1429
+ "cry",
1430
+ "dance",
1431
+ "deliver",
1432
+ "deny",
1433
+ "design",
1434
+ "destroy",
1435
+ "develop",
1436
+ "draw",
1437
+ "dream",
1438
+ "dress",
1439
+ "drive",
1440
+ "drop",
1441
+ "eat",
1442
+ "enjoy",
1443
+ "exercise",
1444
+ "explain",
1445
+ "fail",
1446
+ "fetch",
1447
+ "film",
1448
+ "fix",
1449
+ "flash",
1450
+ "float",
1451
+ "fly",
1452
+ "fold",
1453
+ "fry",
1454
+ "give",
1455
+ "glow",
1456
+ "greet",
1457
+ "grin",
1458
+ "grow",
1459
+ "guess",
1460
+ "hammer",
1461
+ "hang",
1462
+ "happen",
1463
+ "heal",
1464
+ "hear",
1465
+ "help",
1466
+ "hide",
1467
+ "hope",
1468
+ "hug",
1469
+ "hunt",
1470
+ "impress",
1471
+ "invent",
1472
+ "invite",
1473
+ "itch",
1474
+ "jam",
1475
+ "jog",
1476
+ "join",
1477
+ "joke",
1478
+ "judge",
1479
+ "juggle",
1480
+ "jump",
1481
+ "kick",
1482
+ "kiss",
1483
+ "kneel",
1484
+ "knock",
1485
+ "know",
1486
+ "laugh",
1487
+ "lay",
1488
+ "lead",
1489
+ "learn",
1490
+ "leave",
1491
+ "lick",
1492
+ "lie",
1493
+ "listen",
1494
+ "live",
1495
+ "look",
1496
+ "love",
1497
+ "make",
1498
+ "march",
1499
+ "marry",
1500
+ "matter",
1501
+ "melt",
1502
+ "mix",
1503
+ "move",
1504
+ "nail",
1505
+ "notice",
1506
+ "obey",
1507
+ "occur",
1508
+ "own",
1509
+ "pay",
1510
+ "peel",
1511
+ "perform",
1512
+ "play",
1513
+ "poke",
1514
+ "pretend",
1515
+ "promise",
1516
+ "provide",
1517
+ "pull",
1518
+ "punch",
1519
+ "push",
1520
+ "raise",
1521
+ "rescue",
1522
+ "rest",
1523
+ "retire",
1524
+ "return",
1525
+ "rhyme",
1526
+ "roll",
1527
+ "rule",
1528
+ "run",
1529
+ "rush",
1530
+ "say",
1531
+ "scream",
1532
+ "search",
1533
+ "sell",
1534
+ "serve",
1535
+ "shake",
1536
+ "share",
1537
+ "shave",
1538
+ "shine",
1539
+ "shop",
1540
+ "shout",
1541
+ "sin",
1542
+ "sing",
1543
+ "sink",
1544
+ "sip",
1545
+ "sit",
1546
+ "sleep",
1547
+ "slide",
1548
+ "smash",
1549
+ "smell",
1550
+ "smile",
1551
+ "smoke",
1552
+ "sneeze",
1553
+ "sniff",
1554
+ "sort",
1555
+ "speak",
1556
+ "spend",
1557
+ "sparkle",
1558
+ "stand",
1559
+ "stare",
1560
+ "start",
1561
+ "stay",
1562
+ "suffer",
1563
+ "swim",
1564
+ "switch",
1565
+ "talk",
1566
+ "taste",
1567
+ "teach",
1568
+ "tell",
1569
+ "thank",
1570
+ "think",
1571
+ "tickle",
1572
+ "trade",
1573
+ "travel",
1574
+ "try",
1575
+ "turn",
1576
+ "type",
1577
+ "unite",
1578
+ "vanish",
1579
+ "visit",
1580
+ "wait",
1581
+ "walk",
1582
+ "warn",
1583
+ "wash",
1584
+ "watch",
1585
+ "wave",
1586
+ "whisper",
1587
+ "win",
1588
+ "wink",
1589
+ "wonder",
1590
+ "work",
1591
+ "worry",
1592
+ "yawn",
1593
+ "yell"
1594
+ ];
1595
+ const pick = (words) => Effect.map(Random.nextIntBetween(0, words.length), (i) => words[i] ?? words[0] ?? "");
1596
+ const humanId = Effect.all([
1597
+ pick(adjectives),
1598
+ pick(nouns),
1599
+ pick(verbs)
1600
+ ]).pipe(Effect.map((parts) => parts.join("-")));
1601
+ // src/json-edit.ts
1602
+ /**
1603
+ * Formatting-preserving `package.json` edits, as `@changesets/apply-release-plan` does with
1604
+ * jsonc-parser: each edit replaces only the text of an existing value with `JSON.stringify(value)`.
1605
+ */
1606
+ const skipWhitespace = (text, at) => {
1607
+ return at + (/^[ \t\n\r]*/.exec(text.slice(at))?.[0].length ?? 0);
1608
+ };
1609
+ const stringPattern = /^"(?:[^"\\\u0000-\u001f]|\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4}))*"/;
1610
+ const literalPattern = /^(?:true|false|null|-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?)/;
1611
+ const parseString = (text, at) => {
1612
+ const match = stringPattern.exec(text.slice(at));
1613
+ return match === null ? Result.fail(`Invalid string at offset ${at}`) : Result.succeed({
1614
+ value: JSON.parse(match[0]),
1615
+ end: at + match[0].length
1616
+ });
1617
+ };
1618
+ const parseMembers = (text, at, close, member, members) => Result.gen(function* () {
1619
+ const parsed = yield* member(skipWhitespace(text, at));
1620
+ const next = skipWhitespace(text, parsed.end);
1621
+ const all = [...members, parsed.value];
1622
+ if (text[next] === ",") return yield* parseMembers(text, next + 1, close, member, all);
1623
+ if (text[next] === close) return {
1624
+ value: all,
1625
+ end: next + 1
1626
+ };
1627
+ return yield* Result.fail(`Expected "," or "${close}" at offset ${next}`);
1628
+ });
1629
+ const parseValue = (text, at) => Result.gen(function* () {
1630
+ const char = text[at];
1631
+ if (char === "{") {
1632
+ const first = skipWhitespace(text, at + 1);
1633
+ if (text[first] === "}") return {
1634
+ offset: at,
1635
+ length: first + 1 - at,
1636
+ properties: []
1637
+ };
1638
+ const members = yield* parseMembers(text, first, "}", (start) => parseProperty(text, start), []);
1639
+ return {
1640
+ offset: at,
1641
+ length: members.end - at,
1642
+ properties: members.value
1643
+ };
1644
+ }
1645
+ if (char === "[") {
1646
+ const first = skipWhitespace(text, at + 1);
1647
+ if (text[first] === "]") return {
1648
+ offset: at,
1649
+ length: first + 1 - at,
1650
+ properties: void 0
1651
+ };
1652
+ return {
1653
+ offset: at,
1654
+ length: (yield* parseMembers(text, first, "]", (start) => parseItem(text, start), [])).end - at,
1655
+ properties: void 0
1656
+ };
1657
+ }
1658
+ if (char === "\"") return {
1659
+ offset: at,
1660
+ length: (yield* parseString(text, at)).end - at,
1661
+ properties: void 0
1662
+ };
1663
+ const literal = literalPattern.exec(text.slice(at));
1664
+ if (literal === null) return yield* Result.fail(`Unexpected token at offset ${at}`);
1665
+ return {
1666
+ offset: at,
1667
+ length: literal[0].length,
1668
+ properties: void 0
1669
+ };
1670
+ });
1671
+ const parseItem = (text, at) => Result.map(parseValue(text, at), (node) => ({
1672
+ value: node,
1673
+ end: node.offset + node.length
1674
+ }));
1675
+ const parseProperty = (text, at) => Result.gen(function* () {
1676
+ const key = yield* parseString(text, at);
1677
+ const colon = skipWhitespace(text, key.end);
1678
+ if (text[colon] !== ":") return yield* Result.fail(`Expected ":" at offset ${colon}`);
1679
+ const node = yield* parseValue(text, skipWhitespace(text, colon + 1));
1680
+ return {
1681
+ value: {
1682
+ key: key.value,
1683
+ node
1684
+ },
1685
+ end: node.offset + node.length
1686
+ };
1687
+ });
1688
+ const parseDocument = (text) => Result.gen(function* () {
1689
+ const root = yield* parseValue(text, skipWhitespace(text, 0));
1690
+ const end = skipWhitespace(text, root.offset + root.length);
1691
+ if (end !== text.length) return yield* Result.fail(`Unexpected content at offset ${end}`);
1692
+ return root;
1693
+ });
1694
+ const valueNode = (root, keys) => keys.reduce((node, key) => node?.properties?.find((property) => property.key === key)?.node, root);
1695
+ /** Applies every edit to `json`; fails when the JSON is invalid or a key path is missing. */
1696
+ const editJson = (json, edits) => Result.gen(function* () {
1697
+ const root = yield* parseDocument(json);
1698
+ return (yield* Result.all(edits.map((edit) => {
1699
+ const node = valueNode(root, edit.keys);
1700
+ return node === void 0 ? Result.fail(`Key path "${edit.keys.join(".")}" not found in JSON`) : Result.succeed({
1701
+ node,
1702
+ content: JSON.stringify(edit.value)
1703
+ });
1704
+ }))).toSorted((a, b) => b.node.offset - a.node.offset).reduce((text, { node, content }) => text.slice(0, node.offset) + content + text.slice(node.offset + node.length), json);
1705
+ });
1706
+ // src/plan.ts
1707
+ /**
1708
+ * `@changesets/assemble-release-plan` outside pre mode and snapshots: flatten changesets into
1709
+ * releases, then bump dependents and apply fixed/linked groups until nothing changes.
1710
+ * Release order is insertion order, which decides changelog and status output order.
1711
+ */
1712
+ const rank = {
1713
+ major: 3,
1714
+ minor: 2,
1715
+ patch: 1,
1716
+ none: 0
1717
+ };
1718
+ const newVersionOf = (release) => release.type === "none" ? release.oldVersion : increment(release.oldVersion, release.type);
1719
+ const highestType = (releases) => releases.reduce((highest, release) => rank[release.type] > rank[highest] ? release.type : highest, "none");
1720
+ const highestVersion = (group, byName) => group.reduce((highest, name) => {
1721
+ const version = byName.get(name)?.packageJson.version;
1722
+ return version !== void 0 && (highest === void 0 || greaterThan(version, highest)) ? version : highest;
1723
+ }, void 0);
1724
+ const flattenReleases = (changesets, byName, config) => changesets.reduce((releases, changeset) => {
1725
+ changeset.releases.forEach(({ name, type }) => {
1726
+ const pkg = byName.get(name);
1727
+ if (pkg === void 0 || shouldSkipPackage(pkg, skipOptions(config))) return;
1728
+ const release = releases.get(name);
1729
+ if (release === void 0) {
1730
+ releases.set(name, {
1731
+ name,
1732
+ type,
1733
+ oldVersion: pkg.packageJson.version,
1734
+ changesets: [changeset.id]
1735
+ });
1736
+ return;
1737
+ }
1738
+ if (rank[type] > rank[release.type]) release.type = type;
1739
+ release.changesets.push(changeset.id);
1740
+ });
1741
+ return releases;
1742
+ }, /* @__PURE__ */ new Map());
1743
+ const dependencyRanges = (rootDir, dependent, release, dependency) => DEPENDENCY_TYPES.flatMap((depType) => {
1744
+ const raw = dependent.packageJson[depType]?.[release.name];
1745
+ if (!raw) return [];
1746
+ if (!raw.startsWith("workspace:")) return [{
1747
+ depType,
1748
+ versionRange: raw
1749
+ }];
1750
+ const range = raw.slice(10);
1751
+ if (range === "*") return [{
1752
+ depType,
1753
+ versionRange: release.oldVersion
1754
+ }];
1755
+ if (range === "^" || range === "~") return [{
1756
+ depType,
1757
+ versionRange: `${range}${release.oldVersion}`
1758
+ }];
1759
+ if (isValidRange(range)) return [{
1760
+ depType,
1761
+ versionRange: range
1762
+ }];
1763
+ return workspacePathMatches(range, rootDir, dependency) ? [{
1764
+ depType,
1765
+ versionRange: release.oldVersion
1766
+ }] : [];
1767
+ });
1768
+ const dependentType = (ctx, dependentPkg, next) => {
1769
+ if (shouldSkipPackage(dependentPkg, skipOptions(ctx.config))) return "none";
1770
+ const dependency = ctx.byName.get(next.name);
1771
+ if (dependency === void 0 || next.type === "none") return void 0;
1772
+ const existing = ctx.releases.get(dependentPkg.packageJson.name);
1773
+ return dependencyRanges(ctx.rootDir, dependentPkg, next, dependency).reduce((type, { depType, versionRange }) => {
1774
+ if (!((existing === void 0 || existing.type === "none") && (ctx.config.updateInternalDependents === "always" || !satisfies(newVersionOf(next), versionRange)))) return type;
1775
+ if (depType === "devDependencies") return type !== "major" && type !== "minor" && type !== "patch" ? "none" : type;
1776
+ return type !== "major" && type !== "minor" ? "patch" : type;
1777
+ }, void 0);
1778
+ };
1779
+ const determineDependents = (ctx) => {
1780
+ const visit = (queue, updated) => {
1781
+ const [next, ...rest] = queue;
1782
+ if (next === void 0) return updated;
1783
+ const found = (ctx.graph.get(next.name) ?? []).flatMap((dependent) => {
1784
+ const dependentPkg = ctx.byName.get(dependent);
1785
+ if (dependentPkg === void 0) return [];
1786
+ const computed = dependentType(ctx, dependentPkg, next);
1787
+ const type = ctx.releases.get(dependent)?.type === computed ? void 0 : computed;
1788
+ return type === void 0 ? [] : [{
1789
+ name: dependent,
1790
+ type,
1791
+ oldVersion: dependentPkg.packageJson.version
1792
+ }];
1793
+ });
1794
+ const added = found.flatMap(({ name, type, oldVersion }) => {
1795
+ const existing = ctx.releases.get(name);
1796
+ if (existing !== void 0 && type === "major" && existing.type !== "major") {
1797
+ existing.type = "major";
1798
+ return [existing];
1799
+ }
1800
+ const release = {
1801
+ name,
1802
+ type,
1803
+ oldVersion,
1804
+ changesets: []
1805
+ };
1806
+ ctx.releases.set(name, release);
1807
+ return [release];
1808
+ });
1809
+ return visit([...rest, ...added], updated || found.length > 0);
1810
+ };
1811
+ return visit([...ctx.releases.values()], false);
1812
+ };
1813
+ const matchFixedConstraint = (ctx) => ctx.config.fixed.reduce((updated, group) => {
1814
+ const releasing = [...ctx.releases.values()].filter((r) => group.includes(r.name) && r.type !== "none");
1815
+ if (releasing.length === 0) return updated;
1816
+ const type = highestType(releasing);
1817
+ const version = highestVersion(group, ctx.byName);
1818
+ return group.reduce((changed, name) => {
1819
+ const pkg = ctx.byName.get(name);
1820
+ if (pkg === void 0 || shouldSkipPackage(pkg, skipOptions(ctx.config))) return changed;
1821
+ const release = ctx.releases.get(name);
1822
+ if (release === void 0) {
1823
+ ctx.releases.set(name, {
1824
+ name,
1825
+ type,
1826
+ oldVersion: version,
1827
+ changesets: []
1828
+ });
1829
+ return true;
1830
+ }
1831
+ const differs = release.type !== type || release.oldVersion !== version;
1832
+ release.type = type;
1833
+ release.oldVersion = version;
1834
+ return changed || differs;
1835
+ }, updated);
1836
+ }, false);
1837
+ const applyLinks = (ctx) => ctx.config.linked.reduce((updated, group) => {
1838
+ const releasing = [...ctx.releases.values()].filter((r) => group.includes(r.name) && r.type !== "none");
1839
+ if (releasing.length === 0) return updated;
1840
+ const type = highestType(releasing);
1841
+ const version = highestVersion(group, ctx.byName);
1842
+ return releasing.reduce((changed, release) => {
1843
+ const differs = release.type !== type || release.oldVersion !== version;
1844
+ release.type = type;
1845
+ release.oldVersion = version;
1846
+ return changed || differs;
1847
+ }, updated);
1848
+ }, false);
1849
+ /** Rejects a changeset naming an unknown package, or mixing skipped and released packages. */
1850
+ const relevantChangesets = (changesets, byName, config) => {
1851
+ const [first] = changesets.flatMap((changeset) => {
1852
+ const unknown = changeset.releases.find((release) => !byName.has(release.name));
1853
+ if (unknown !== void 0) return [`Found changeset ${changeset.id} for package ${unknown.name} which is not in the workspace`];
1854
+ const skipped = changeset.releases.filter((r) => {
1855
+ const pkg = byName.get(r.name);
1856
+ return pkg !== void 0 && shouldSkipPackage(pkg, skipOptions(config));
1857
+ });
1858
+ const kept = changeset.releases.filter((r) => !skipped.includes(r));
1859
+ return skipped.length > 0 && kept.length > 0 ? [`Found mixed changeset ${changeset.id}\nFound ignored packages: ${skipped.map((r) => r.name).join(" ")}\nFound not ignored packages: ${kept.map((r) => r.name).join(" ")}\nMixed changesets that contain both ignored and not ignored packages are not allowed`] : [];
1860
+ });
1861
+ return first === void 0 ? Result.succeed(changesets) : Result.fail(first);
1862
+ };
1863
+ const assembleReleasePlan = (changesets, packages, config) => {
1864
+ const byName = new Map(packages.packages.map((pkg) => [pkg.packageJson.name, pkg]));
1865
+ return Result.map(relevantChangesets(changesets, byName, config), (relevant) => {
1866
+ const ctx = {
1867
+ releases: flattenReleases(relevant, byName, config),
1868
+ byName,
1869
+ rootDir: packages.rootDir,
1870
+ graph: dependentsGraph(packages, { bumpVersionsWithWorkspaceProtocolOnly: config.bumpVersionsWithWorkspaceProtocolOnly }).graph,
1871
+ config
1872
+ };
1873
+ const settle = () => {
1874
+ const dependentAdded = determineDependents(ctx);
1875
+ const fixedUpdated = matchFixedConstraint(ctx);
1876
+ const linksUpdated = applyLinks(ctx);
1877
+ if (dependentAdded || fixedUpdated || linksUpdated) settle();
1878
+ };
1879
+ settle();
1880
+ return {
1881
+ changesets: relevant,
1882
+ releases: [...ctx.releases.values()].map((release) => ({
1883
+ name: release.name,
1884
+ type: release.type,
1885
+ oldVersion: release.oldVersion,
1886
+ changesets: release.changesets,
1887
+ newVersion: newVersionOf(release)
1888
+ }))
1889
+ };
1890
+ });
1891
+ };
1892
+ // src/workspace.ts
1893
+ /**
1894
+ * Workspace discovery as `@manypkg/get-packages` does it for package.json `workspaces` with a
1895
+ * bun, yarn or npm lockfile: the nearest such ancestor is the root, else the nearest package.json
1896
+ * is a single-package root. Packages are the matching dirs with a package.json, sorted by path.
1897
+ */
1898
+ const Ranges = Schema.optionalKey(Schema.Record(Schema.String, Schema.String));
1899
+ const Manifest = Schema.Struct({
1900
+ name: Schema.optionalKey(Schema.String),
1901
+ version: Schema.optionalKey(Schema.String),
1902
+ private: Schema.optionalKey(Schema.Boolean),
1903
+ workspaces: Schema.optionalKey(Schema.Union([Schema.Array(Schema.String), Schema.Struct({ packages: Schema.optionalKey(Schema.Array(Schema.String)) })])),
1904
+ dependencies: Ranges,
1905
+ devDependencies: Ranges,
1906
+ peerDependencies: Ranges,
1907
+ optionalDependencies: Ranges
1908
+ });
1909
+ const decodeManifest = Schema.decodeUnknownEffect(Schema.fromJsonString(Manifest));
1910
+ const isList = (value) => Array.isArray(value);
1911
+ const workspaceGlobs = ({ workspaces }) => workspaces === void 0 ? void 0 : isList(workspaces) ? workspaces : workspaces.packages ?? [];
1912
+ const toPackageJson = (manifest, name) => ({
1913
+ ...manifest,
1914
+ name
1915
+ });
1916
+ const readManifest = Effect.fn("readManifest")(function* (dir) {
1917
+ const fs = yield* FileSystem.FileSystem;
1918
+ const file = (yield* Path.Path).join(dir, "package.json");
1919
+ const text = yield* fs.readFileString(file);
1920
+ return yield* decodeManifest(text).pipe(Effect.mapError((cause) => new WorkspaceError({
1921
+ message: `Could not read ${file}: ${cause.message}`,
1922
+ path: file,
1923
+ cause
1924
+ })));
1925
+ });
1926
+ const manifestIn = (dir) => Effect.gen(function* () {
1927
+ const fs = yield* FileSystem.FileSystem;
1928
+ const path = yield* Path.Path;
1929
+ return (yield* fs.exists(path.join(dir, "package.json"))) ? Option.some(yield* readManifest(dir)) : Option.none();
1930
+ });
1931
+ const isWorkspaceRoot = (dir, manifest) => Effect.gen(function* () {
1932
+ const fs = yield* FileSystem.FileSystem;
1933
+ const path = yield* Path.Path;
1934
+ if (manifest.workspaces === void 0) return false;
1935
+ const has = (file) => fs.exists(path.join(dir, file));
1936
+ if ((yield* has("bun.lock")) || (yield* has("bun.lockb")) || (yield* has("yarn.lock"))) return true;
1937
+ return isList(manifest.workspaces) && (yield* has("package-lock.json"));
1938
+ });
1939
+ const findRoot = (dir, nearest) => Effect.gen(function* () {
1940
+ const path = yield* Path.Path;
1941
+ const manifest = yield* manifestIn(dir);
1942
+ if (Option.isSome(manifest) && (yield* isWorkspaceRoot(dir, manifest.value))) return Option.some({
1943
+ dir,
1944
+ manifest: manifest.value
1945
+ });
1946
+ const nextNearest = Option.isNone(nearest) && Option.isSome(manifest) ? Option.some({
1947
+ dir,
1948
+ manifest: manifest.value
1949
+ }) : nearest;
1950
+ const parent = path.dirname(dir);
1951
+ return parent === dir ? nextNearest : yield* findRoot(parent, nextNearest);
1952
+ });
1953
+ const expandGlobs = Effect.fn("expandGlobs")(function* (globs, rootDir) {
1954
+ const fs = yield* FileSystem.FileSystem;
1955
+ const path = yield* Path.Path;
1956
+ const exclude = ["**/node_modules", ...globs.filter((g) => g.startsWith("!")).map((g) => g.slice(1))];
1957
+ const matched = yield* Effect.forEach(globs.filter((g) => !g.startsWith("!")), (glob) => fs.glob(glob.replace(/\/+$/, ""), {
1958
+ root: rootDir,
1959
+ exclude
1960
+ }));
1961
+ const candidates = [...new Set(matched.flat().map((relative) => path.resolve(rootDir, relative)))].toSorted();
1962
+ return (yield* Effect.forEach(candidates, (dir) => Effect.gen(function* () {
1963
+ if ((yield* fs.stat(dir)).type !== "Directory") return Option.none();
1964
+ const manifest = yield* manifestIn(dir);
1965
+ return Option.map(manifest, (value) => ({
1966
+ dir,
1967
+ manifest: value
1968
+ }));
1969
+ }))).flatMap(Option.toArray);
1970
+ });
1971
+ /** Finds the workspace containing `cwd` and reads every package manifest in it. */
1972
+ const getPackages = Effect.fn("getPackages")(function* (cwd) {
1973
+ const path = yield* Path.Path;
1974
+ const start = path.resolve(cwd);
1975
+ const root = yield* findRoot(start, Option.none());
1976
+ if (Option.isNone(root)) return yield* new WorkspaceError({
1977
+ message: `No package.json found from ${start} upwards`,
1978
+ path: start
1979
+ });
1980
+ const rootDir = root.value.dir;
1981
+ const rootPackage = {
1982
+ dir: rootDir,
1983
+ relativeDir: ".",
1984
+ packageJson: toPackageJson(root.value.manifest, root.value.manifest.name ?? "")
1985
+ };
1986
+ const globs = workspaceGlobs(root.value.manifest);
1987
+ if (globs === void 0) return {
1988
+ rootDir,
1989
+ rootPackage,
1990
+ packages: [rootPackage]
1991
+ };
1992
+ const found = yield* expandGlobs(globs, rootDir);
1993
+ const unnamed = found.filter(({ manifest }) => manifest.name === void 0);
1994
+ if (unnamed.length > 0) return yield* new WorkspaceError({
1995
+ message: `The following package.jsons are missing the "name" field:\n${unnamed.map(({ dir }) => path.join(path.relative(rootDir, dir), "package.json")).toSorted().join("\n")}`,
1996
+ path: rootDir
1997
+ });
1998
+ return {
1999
+ rootDir,
2000
+ rootPackage,
2001
+ packages: found.map(({ dir, manifest }) => ({
2002
+ dir,
2003
+ relativeDir: path.relative(rootDir, dir),
2004
+ packageJson: toPackageJson(manifest, manifest.name ?? "")
2005
+ }))
2006
+ };
2007
+ });
2008
+ // src/changesets.ts
2009
+ /**
2010
+ * The `add`, `version` and `status` commands of `@changesets/cli` 3.x, reading and writing the
2011
+ * same files. Pre mode, snapshots, `commit` and `publish` are out of scope and fail loudly.
2012
+ */
2013
+ const ignoredMarkdown = (file) => /^README\.md$/i.test(file) || file === "AGENTS.md" || file === "CLAUDE.md" || file === "GEMINI.md";
2014
+ const encoder = new TextEncoder();
2015
+ /** libuv's `scandir` order (`strcmp` on UTF-8), which is what `fs.readdir` returns under Node. */
2016
+ const byteOrder = (a, b) => {
2017
+ const left = encoder.encode(a);
2018
+ const right = encoder.encode(b);
2019
+ const index = left.findIndex((byte, i) => byte !== right[i]);
2020
+ if (index === -1) return left.length - right.length;
2021
+ return (left[index] ?? 0) - (right[index] ?? -1);
2022
+ };
2023
+ const readSorted = (fs, dir) => Effect.map(fs.readDirectory(dir), (entries) => entries.toSorted(byteOrder));
2024
+ const builtinChangelogs = /* @__PURE__ */ new Set(["@changesets/cli/changelog", "@changesets/changelog-git"]);
2025
+ const decodeConfig = Schema.decodeUnknownEffect(Schema.fromJsonString(WrittenConfig));
2026
+ var Changesets = class extends Context.Service()("@maple-dev/changesets/Changesets", { make: Effect.gen(function* () {
2027
+ const fs = yield* FileSystem.FileSystem;
2028
+ const path = yield* Path.Path;
2029
+ const git = yield* Git;
2030
+ const formatter = yield* Formatter;
2031
+ const workspaceError = (file) => (cause) => new WorkspaceError({
2032
+ message: `Could not access ${file}: ${cause.message}`,
2033
+ path: file,
2034
+ cause
2035
+ });
2036
+ const changesetDir = (packages) => path.join(packages.rootDir, ".changeset");
2037
+ const ensureChangesetFolder = (packages) => Effect.flatMap(fs.exists(changesetDir(packages)).pipe(Effect.mapError(workspaceError(changesetDir(packages)))), (exists) => exists ? Effect.void : Effect.fail(new CommandExit({
2038
+ code: 1,
2039
+ message: "There is no .changeset folder.\nIf this is the first time Changesets have been used in this project, run `changeset init` to get set up.\nIf you expected there to be changesets, you should check git history for when the folder was removed to ensure you do not lose any configuration."
2040
+ })));
2041
+ /** Reads the workspace and `.changeset/config.json`; config rule violations fail. */
2042
+ const load = Effect.fn("Changesets.load")(function* (cwd) {
2043
+ const packages = yield* getPackages(cwd).pipe(Effect.catchTag("PlatformError", (e) => workspaceError(cwd)(e)));
2044
+ yield* ensureChangesetFolder(packages);
2045
+ const file = path.join(changesetDir(packages), "config.json");
2046
+ const text = yield* fs.readFileString(file).pipe(Effect.mapError(workspaceError(file)));
2047
+ const written = yield* decodeConfig(text).pipe(Effect.mapError((cause) => new ConfigError({
2048
+ message: `Found issues in your config:\n- ${cause.message}`,
2049
+ problems: [cause.message]
2050
+ })));
2051
+ const config = normalizeConfig(written, packages.packages.map((pkg) => pkg.packageJson.name));
2052
+ const { errors, warnings } = validateConfig(config, written, packages);
2053
+ if (errors.length > 0) return yield* new ConfigError({
2054
+ message: `Found issues in your config:\n- ${[...warnings, ...errors].join("\n- ")}`,
2055
+ problems: [...warnings, ...errors]
2056
+ });
2057
+ return {
2058
+ packages,
2059
+ config,
2060
+ warnings
2061
+ };
2062
+ });
2063
+ const refuseUnsupported = Effect.fn("Changesets.refuseUnsupported")(function* (project) {
2064
+ const preState = path.join(changesetDir(project.packages), "pre.json");
2065
+ if (yield* fs.exists(preState).pipe(Effect.mapError(workspaceError(preState)))) return yield* new UnsupportedFeatureError({
2066
+ feature: "pre",
2067
+ message: "Pre mode (.changeset/pre.json) is not supported by this changesets implementation."
2068
+ });
2069
+ if (project.config.commit !== false) return yield* new UnsupportedFeatureError({
2070
+ feature: "commit",
2071
+ message: "The \"commit\" option is not supported by this changesets implementation; set it to false."
2072
+ });
2073
+ });
2074
+ /** `@changesets/read`: every changeset in `.changeset` and `.changeset/pre`, in directory order. */
2075
+ const readChangesets = Effect.fn("Changesets.readChangesets")(function* (packages, since) {
2076
+ const base = changesetDir(packages);
2077
+ const top = yield* readSorted(fs, base).pipe(Effect.mapError(workspaceError(base)));
2078
+ const preDir = path.join(base, "pre");
2079
+ const pre = (yield* fs.exists(preDir).pipe(Effect.mapError(workspaceError(preDir)))) ? (yield* readSorted(fs, preDir).pipe(Effect.mapError(workspaceError(preDir)))).map((f) => `pre/${f}`) : [];
2080
+ const changed = since === void 0 ? void 0 : (yield* git.changedChangesetFilesSince(since, base)).map((file) => file.replace(/^.*\.changeset\//, ""));
2081
+ const files = [...top, ...pre].filter((file) => changed === void 0 || changed.includes(file)).filter((file) => {
2082
+ const name = path.basename(file);
2083
+ return !name.startsWith(".") && name.endsWith(".md") && !ignoredMarkdown(name);
2084
+ });
2085
+ return yield* Effect.forEach(files, (file) => Effect.gen(function* () {
2086
+ const full = path.join(base, file);
2087
+ const contents = yield* fs.readFileString(full).pipe(Effect.mapError(workspaceError(full)));
2088
+ const parsed = parseChangesetFile(contents);
2089
+ if (Result.isFailure(parsed)) return yield* new ChangesetParseError({
2090
+ message: parsed.failure,
2091
+ file: full
2092
+ });
2093
+ return {
2094
+ ...parsed.success,
2095
+ id: file.replace(/\.md$/, "")
2096
+ };
2097
+ }));
2098
+ });
2099
+ const plan = (changesets, packages, config) => {
2100
+ const result = assembleReleasePlan(changesets, packages, config);
2101
+ return Result.isSuccess(result) ? Effect.succeed(result.success) : Effect.fail(new ReleasePlanError({ message: result.failure }));
2102
+ };
2103
+ const logGraphWarnings = (packages, config) => Effect.forEach(dependentsGraph(packages, { bumpVersionsWithWorkspaceProtocolOnly: config.bumpVersionsWithWorkspaceProtocolOnly }).invalid, (line) => Console.error(line), { discard: true });
2104
+ const changelogFunctions = Effect.fn("Changesets.changelogFunctions")(function* (reference, packages) {
2105
+ const [specifier] = reference;
2106
+ if (builtinChangelogs.has(specifier)) return gitChangelog;
2107
+ if (!specifier.startsWith(".") && !path.isAbsolute(specifier)) return yield* new UnsupportedFeatureError({
2108
+ feature: "changelog",
2109
+ message: `Changelog module "${specifier}" is not supported; use "@changesets/cli/changelog", false, or a path relative to .changeset.`
2110
+ });
2111
+ const file = path.resolve(changesetDir(packages), specifier);
2112
+ const loaded = yield* Effect.tryPromise({
2113
+ try: () => import(file),
2114
+ catch: (cause) => new ChangelogError({
2115
+ message: `Could not load ${file}`,
2116
+ module: specifier,
2117
+ cause
2118
+ })
2119
+ });
2120
+ return yield* changelogFromModule(loaded, specifier);
2121
+ });
2122
+ const applyPlan = Effect.fn("Changesets.applyPlan")(function* (releasePlan, packages, config) {
2123
+ const byName = new Map(packages.packages.map((pkg) => [pkg.packageJson.name, pkg]));
2124
+ const releases = releasePlan.releases.flatMap((release) => {
2125
+ const pkg = byName.get(release.name);
2126
+ return pkg === void 0 ? [] : [{
2127
+ ...release,
2128
+ ...pkg,
2129
+ relativeDir: relativeDirOf(packages.rootDir, pkg)
2130
+ }];
2131
+ });
2132
+ const updateOptions = {
2133
+ rootDir: packages.rootDir,
2134
+ updateInternalDependencies: config.updateInternalDependencies,
2135
+ onlyUpdatePeerDependentsWhenOutOfRange: config.onlyUpdatePeerDependentsWhenOutOfRange
2136
+ };
2137
+ const changelogs = yield* changelogsFor(releasePlan, releases, packages, config, updateOptions);
2138
+ const editOptions = {
2139
+ ...updateOptions,
2140
+ bumpVersionsWithWorkspaceProtocolOnly: config.bumpVersionsWithWorkspaceProtocolOnly
2141
+ };
2142
+ const written = yield* Effect.forEach(releases, (release, index) => Effect.gen(function* () {
2143
+ const versionEdit = release.newVersion === void 0 ? [] : [{
2144
+ keys: ["version"],
2145
+ value: release.newVersion
2146
+ }];
2147
+ yield* updatePackageJson(release, [...dependencyEdits(release.packageJson, releases, editOptions), ...versionEdit]);
2148
+ const changelog = changelogs[index];
2149
+ if (changelog === void 0 || changelog.length === 0) return Option.none();
2150
+ const file = path.resolve(release.dir, "CHANGELOG.md");
2151
+ const existing = yield* fs.readFileString(file).pipe(Effect.map(Option.some), Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(Option.none())), Effect.mapError(workspaceError(file)));
2152
+ yield* fs.writeFileString(file, prependEntry(Option.getOrUndefined(existing), changelog, release.name)).pipe(Effect.mapError(workspaceError(file)));
2153
+ return Option.some(file);
2154
+ }));
2155
+ yield* updatePackageJson(packages.rootPackage, dependencyEdits(packages.rootPackage.packageJson, releases, editOptions));
2156
+ yield* formatter.format(config.format, packages.rootDir, written.flatMap(Option.toArray));
2157
+ yield* Effect.forEach(releasePlan.changesets, (changeset) => Effect.gen(function* () {
2158
+ const file = path.resolve(changesetDir(packages), `${changeset.id}.md`);
2159
+ if (changeset.releases.some((r) => {
2160
+ const pkg = byName.get(r.name);
2161
+ return pkg !== void 0 && shouldSkipPackage(pkg, skipOptions(config));
2162
+ }) || !(yield* fs.exists(file).pipe(Effect.mapError(workspaceError(file))))) return;
2163
+ yield* fs.remove(file, { force: true }).pipe(Effect.mapError(workspaceError(file)));
2164
+ }), { discard: true });
2165
+ });
2166
+ const changelogsFor = Effect.fn("Changesets.changelogsFor")(function* (releasePlan, releases, packages, config, options) {
2167
+ if (config.changelog === false) return releases.map(() => void 0);
2168
+ const functions = yield* changelogFunctions(config.changelog, packages);
2169
+ const commits = yield* git.commitsThatAddFiles(releasePlan.changesets.map((changeset) => `.changeset/${changeset.id}.md`), packages.rootDir);
2170
+ const withCommits = releasePlan.changesets.map((changeset, i) => ({
2171
+ ...changeset,
2172
+ commit: commits[i]
2173
+ }));
2174
+ return yield* Effect.forEach(releases, (release) => changelogEntry(release, releases, withCommits, functions, config.changelog === false ? null : config.changelog[1], options));
2175
+ });
2176
+ const updatePackageJson = (pkg, edits) => Effect.gen(function* () {
2177
+ if (edits.length === 0) return;
2178
+ const file = path.resolve(pkg.dir, "package.json");
2179
+ const text = yield* fs.readFileString(file).pipe(Effect.mapError(workspaceError(file)));
2180
+ const edited = editJson(text, edits);
2181
+ if (Result.isFailure(edited)) return yield* new JsonEditError({
2182
+ message: edited.failure,
2183
+ file
2184
+ });
2185
+ yield* fs.writeFileString(file, edited.success).pipe(Effect.mapError(workspaceError(file)));
2186
+ });
2187
+ /** `changeset version [--ignore pkg]`. */
2188
+ const version = Effect.fn("Changesets.version")(function* (options) {
2189
+ const project = yield* load(options.cwd);
2190
+ yield* Effect.forEach(project.warnings, (warning) => Console.warn(warning), { discard: true });
2191
+ const { packages } = project;
2192
+ const messages = [];
2193
+ const cliIgnore = options.ignore !== void 0 && options.ignore.length > 0 ? options.ignore : void 0;
2194
+ if (cliIgnore !== void 0 && project.config.ignore.length > 0) messages.push("It looks like you are trying to use the `--ignore` option while ignore is defined in the config file. This is currently not allowed, you can only use one of them at a time.");
2195
+ const config = {
2196
+ ...project.config,
2197
+ ignore: cliIgnore ?? project.config.ignore
2198
+ };
2199
+ const names = new Set(packages.packages.map((pkg) => pkg.packageJson.name));
2200
+ (cliIgnore ?? []).filter((name) => !names.has(name)).forEach((name) => messages.push(`The package ${name} is passed to the \`--ignore\` option but it is not found in the project. You may have misspelled the package name.`));
2201
+ skippedDependentErrors(packages, config).forEach(({ dependent, skipped }) => messages.push(`The package ${dependent} depends on the skipped package ${skipped} (either by \`ignore\` option or by \`privatePackages.version\`), but ${dependent} is not being skipped. Please pass ${dependent} to the --ignore flag.`));
2202
+ if (messages.length > 0) return yield* new CommandExit({
2203
+ code: 1,
2204
+ message: messages.join("\n")
2205
+ });
2206
+ yield* refuseUnsupported(project);
2207
+ const changesets = yield* readChangesets(packages);
2208
+ if (changesets.length === 0) return yield* new CommandExit({
2209
+ code: 1,
2210
+ message: "No unreleased changesets found."
2211
+ });
2212
+ yield* logGraphWarnings(packages, config);
2213
+ const releasePlan = yield* plan(changesets, packages, config);
2214
+ yield* applyPlan(releasePlan, packages, config);
2215
+ return releasePlan;
2216
+ });
2217
+ /** Workspace packages changed since `ref` that changesets would version. */
2218
+ const changedPackages = Effect.fn("Changesets.changedPackages")(function* (project, ref) {
2219
+ const files = [...yield* git.changedFilesSince(ref ?? project.config.baseBranch, project.packages.rootDir)];
2220
+ return project.packages.packages.toSorted((a, b) => b.dir.length - a.dir.length).filter((pkg) => {
2221
+ const owned = files.filter((file) => !path.relative(pkg.dir, file).startsWith(".."));
2222
+ owned.forEach((file) => files.splice(files.indexOf(file), 1));
2223
+ const relative = owned.map((file) => file.slice(pkg.dir.length + 1));
2224
+ return relative.length > 0 && matchesSome(relative, project.config.changedFilePatterns);
2225
+ }).filter((pkg) => !shouldSkipPackage(pkg, skipOptions(project.config)));
2226
+ });
2227
+ return {
2228
+ load,
2229
+ refuseUnsupported,
2230
+ readChangesets,
2231
+ version,
2232
+ status: Effect.fn("Changesets.status")(function* (options) {
2233
+ const project = yield* load(options.cwd);
2234
+ yield* refuseUnsupported(project);
2235
+ const changesets = yield* readChangesets(project.packages, options.since);
2236
+ const releasePlan = yield* plan(changesets, project.packages, project.config);
2237
+ if ((yield* changedPackages(project, options.since)).length > 0 && releasePlan.changesets.length === 0) return yield* new CommandExit({
2238
+ code: 1,
2239
+ message: "Some packages have been changed but no changesets were found. Run `changeset add` to resolve this error.\nIf this change doesn't need a release, run `changeset add --empty`."
2240
+ });
2241
+ if (options.output !== void 0) {
2242
+ const file = path.resolve(options.cwd, options.output);
2243
+ yield* fs.writeFileString(file, JSON.stringify(releasePlan, void 0, 2)).pipe(Effect.mapError(workspaceError(file)));
2244
+ }
2245
+ return releasePlan;
2246
+ }),
2247
+ changedPackages,
2248
+ writeChangeset: Effect.fn("Changesets.writeChangeset")(function* (project, changeset) {
2249
+ const base = changesetDir(project.packages);
2250
+ const pickId = Effect.flatMap(humanId, (id) => Effect.flatMap(fs.exists(path.join(base, `${id}.md`)).pipe(Effect.mapError(workspaceError(base))), (taken) => taken ? pickId : Effect.succeed(id)));
2251
+ const id = yield* pickId;
2252
+ const file = path.join(base, `${id}.md`);
2253
+ const contents = `---\n${changeset.releases.map((r) => `"${r.name}": ${r.type}`).join("\n")}\n---\n\n${changeset.summary}\n`;
2254
+ yield* fs.makeDirectory(base, { recursive: true }).pipe(Effect.mapError(workspaceError(base)));
2255
+ yield* fs.writeFileString(file, contents).pipe(Effect.mapError(workspaceError(file)));
2256
+ yield* formatter.format(project.config.format, project.packages.rootDir, [file]);
2257
+ return {
2258
+ id,
2259
+ file
2260
+ };
2261
+ })
2262
+ };
2263
+ }) }) {
2264
+ static layer = Layer.effect(this, this.make).pipe(Layer.provide([Git.layer, Formatter.layer]));
2265
+ };
2266
+ const matchesSome = (paths, patterns) => paths.some((file) => matchesPatterns(file.replaceAll("\\", "/"), patterns));
2267
+ const isModuleFunction = (value) => typeof value === "function";
2268
+ const findFunctions = (module, depth) => {
2269
+ if (Predicate.hasProperty(module, "default") && module.default && depth < 2) return findFunctions(module.default, depth + 1);
2270
+ return Predicate.hasProperty(module, "getReleaseLine") && Predicate.hasProperty(module, "getDependencyReleaseLine") && isModuleFunction(module.getReleaseLine) && isModuleFunction(module.getDependencyReleaseLine) ? Option.some({
2271
+ getReleaseLine: module.getReleaseLine,
2272
+ getDependencyReleaseLine: module.getDependencyReleaseLine
2273
+ }) : Option.none();
2274
+ };
2275
+ const callModule = (fn, args, specifier) => Effect.tryPromise({
2276
+ try: () => Promise.resolve(fn(...args)),
2277
+ catch: (cause) => new ChangelogError({
2278
+ message: `Changelog module ${specifier} failed`,
2279
+ module: specifier,
2280
+ cause
2281
+ })
2282
+ }).pipe(Effect.map((line) => typeof line === "string" ? line : String(line ?? "")));
2283
+ /** Adapts a changelog module (`getReleaseLine`, `getDependencyReleaseLine`) to Effects. */
2284
+ const changelogFromModule = (module, specifier) => Option.match(findFunctions(module, 0), {
2285
+ onNone: () => Effect.fail(new ChangelogError({
2286
+ message: "Could not resolve changelog generation functions",
2287
+ module: specifier
2288
+ })),
2289
+ onSome: ({ getReleaseLine, getDependencyReleaseLine }) => Effect.succeed({
2290
+ getReleaseLine: (changeset, type, options) => callModule(getReleaseLine, [
2291
+ changeset,
2292
+ type,
2293
+ options
2294
+ ], specifier),
2295
+ getDependencyReleaseLine: (changesets, dependencies, options) => callModule(getDependencyReleaseLine, [
2296
+ changesets,
2297
+ dependencies,
2298
+ options
2299
+ ], specifier)
2300
+ })
2301
+ });
2302
+ // src/cli.ts
2303
+ /**
2304
+ * The `changeset` command line: `add` (also the default), `version` and `status` with the
2305
+ * `@changesets/cli` 3.x flags. Editors are spawned without a shell, `$EDITOR` split on whitespace.
2306
+ */
2307
+ const version = "0.1.0";
2308
+ const listFlag = (name, description) => Flag.String(name).pipe(Flag.withDescription(description), Flag.atLeast(0), Flag.map((values) => values.flatMap((value) => value.split(",").map((part) => part.trim()).filter(Boolean))));
2309
+ const addFlags = {
2310
+ empty: Flag.Boolean("empty").pipe(Flag.withDescription("Add an empty changeset"), Flag.withDefault(false)),
2311
+ open: Flag.Boolean("open").pipe(Flag.withDescription("Open the changeset in $VISUAL or $EDITOR after creating it"), Flag.withDefault(false)),
2312
+ since: Flag.String("since").pipe(Flag.withDescription("Detect changed packages since the provided git ref"), Flag.optional),
2313
+ message: Flag.String("message").pipe(Flag.withAlias("m"), Flag.withDescription("Directly provide a message to the changeset"), Flag.optional),
2314
+ major: listFlag("major", "Package(s) to major bump (comma-separated)"),
2315
+ minor: listFlag("minor", "Package(s) to minor bump (comma-separated)"),
2316
+ patch: listFlag("patch", "Package(s) to patch bump (comma-separated)")
2317
+ };
2318
+ const cwd = Effect.gen(function* () {
2319
+ return (yield* Path.Path).resolve(".");
2320
+ });
2321
+ const success = (message) => Console.log(`šŸ¦‹ ${message}`);
2322
+ const warn = (message) => Console.warn(`šŸ¦‹ warn ${message.split("\n").join("\nšŸ¦‹ warn ")}`);
2323
+ const ask = (prompt) => Prompt.run(prompt).pipe(Effect.catchTag("QuitError", () => Effect.fail(new CommandExit({
2324
+ code: 0,
2325
+ message: "Canceled... šŸ‘‹"
2326
+ }))));
2327
+ const editorCommand = Effect.gen(function* () {
2328
+ const visual = yield* Config.option(Config.String("VISUAL"));
2329
+ const editor = yield* Config.option(Config.String("EDITOR"));
2330
+ return Option.orElse(visual, () => editor).pipe(Option.map((value) => value.trim().split(/\s+/)), Option.filter((parts) => parts.length > 0 && parts[0] !== ""));
2331
+ }).pipe(Effect.orElseSucceed(() => Option.none()));
2332
+ /** Opens `file` in the user's editor and waits for it to exit. */
2333
+ const openInEditor = (file) => Effect.gen(function* () {
2334
+ const command = yield* editorCommand;
2335
+ if (Option.isNone(command)) return false;
2336
+ const [bin = "", ...args] = command.value;
2337
+ const exit = yield* (yield* ChildProcessSpawner.ChildProcessSpawner).exitCode(ChildProcess.make(bin, [...args, file], {
2338
+ stdin: "inherit",
2339
+ stdout: "inherit",
2340
+ stderr: "inherit"
2341
+ }));
2342
+ return Number(exit) === 0;
2343
+ }).pipe(Effect.orElseSucceed(() => false));
2344
+ const summaryFromEditor = Effect.gen(function* () {
2345
+ const fs = yield* FileSystem.FileSystem;
2346
+ const path = yield* Path.Path;
2347
+ const dir = yield* fs.makeTempDirectory();
2348
+ const file = path.join(dir, "changeset.md");
2349
+ yield* fs.writeFileString(file, "\n\n# Please enter a summary for your changes.\n# An empty message aborts the editor.");
2350
+ const contents = (yield* openInEditor(file)) ? yield* fs.readFileString(file) : "";
2351
+ yield* fs.remove(dir, { recursive: true });
2352
+ return contents.replace(/^#.*\n?/gm, "").replace(/\n+$/g, "").trim();
2353
+ }).pipe(Effect.orElseSucceed(() => ""));
2354
+ const askSummary = Effect.gen(function* () {
2355
+ const typed = yield* ask(Prompt.String({ message: "Please enter a summary for this change (this will be in the changelogs). (submit nothing to open an external editor)" }));
2356
+ if (typed.length > 0) return typed;
2357
+ const edited = yield* summaryFromEditor;
2358
+ if (edited.length > 0) return edited;
2359
+ return yield* ask(Prompt.String({
2360
+ message: "Did not find a summary in the edited file. Please enter one:",
2361
+ validate: (value) => value.length === 0 ? Effect.fail("Can't be empty.") : Effect.succeed(value)
2362
+ }));
2363
+ });
2364
+ const confirmMajor = (pkg) => pkg.packageJson.version !== void 0 && lessThan(pkg.packageJson.version, "1.0.0") ? ask(Prompt.Confirm({
2365
+ message: `The major version of ${pkg.packageJson.name} will be its first major release (1.0.0). Are you sure you want to release the first major version of ${pkg.packageJson.name}?`,
2366
+ initial: true
2367
+ })) : Effect.succeed(true);
2368
+ const releasesFromFlags = (options, packages) => {
2369
+ const names = new Set(packages.map((pkg) => pkg.packageJson.name));
2370
+ const byType = [
2371
+ ["major", [...new Set(options.major)]],
2372
+ ["minor", [...new Set(options.minor)]],
2373
+ ["patch", [...new Set(options.patch)]]
2374
+ ];
2375
+ const unknown = byType.flatMap(([type, list]) => list.filter((name) => !names.has(name)).map((name) => `The package ${name} is passed to the \`--${type}\` option but it is not found in the project. You may have misspelled the package name.`));
2376
+ const duplicated = [...names].flatMap((name) => {
2377
+ const flags = byType.filter(([, list]) => list.includes(name)).map(([type]) => `\`--${type}\``);
2378
+ return flags.length > 1 ? [`The package ${name} is passed to multiple release type options: ${flags.join(", ")}. Please select only one release type for this package.`] : [];
2379
+ });
2380
+ const problems = [...unknown, ...duplicated];
2381
+ return problems.length > 0 ? Effect.fail(new CommandExit({
2382
+ code: 1,
2383
+ message: problems.join("\n")
2384
+ })) : Effect.succeed(byType.flatMap(([type, list]) => list.map((name) => ({
2385
+ name,
2386
+ type
2387
+ }))));
2388
+ };
2389
+ const pickReleases = Effect.fn("pickReleases")(function* (changed, packages) {
2390
+ const byName = new Map(packages.map((pkg) => [pkg.packageJson.name, pkg]));
2391
+ const label = (name) => `${name}@${byName.get(name)?.packageJson.version ?? ""}`;
2392
+ const [only, ...others] = packages;
2393
+ if (only !== void 0 && others.length === 0) {
2394
+ const pkg = only;
2395
+ const type = yield* ask(Prompt.Select({
2396
+ message: `What kind of change is this for ${pkg.packageJson.name}? (current version is ${pkg.packageJson.version})`,
2397
+ choices: [
2398
+ {
2399
+ title: "patch",
2400
+ value: "patch"
2401
+ },
2402
+ {
2403
+ title: "minor",
2404
+ value: "minor"
2405
+ },
2406
+ {
2407
+ title: "major",
2408
+ value: "major"
2409
+ }
2410
+ ]
2411
+ }));
2412
+ if (type === "major" && !(yield* confirmMajor(pkg))) return yield* new CommandExit({
2413
+ code: 1,
2414
+ message: "Aborted."
2415
+ });
2416
+ return [{
2417
+ name: pkg.packageJson.name,
2418
+ type
2419
+ }];
2420
+ }
2421
+ const sorted = packages.toSorted((a, b) => a.packageJson.name.localeCompare(b.packageJson.name));
2422
+ const ordered = [...sorted.filter((pkg) => changed.includes(pkg.packageJson.name)), ...sorted.filter((pkg) => !changed.includes(pkg.packageJson.name))];
2423
+ const chosen = (yield* ask(Prompt.MultiSelect({
2424
+ message: "Which packages were affected by the changes you made?",
2425
+ min: 1,
2426
+ choices: ordered.map((pkg) => ({
2427
+ title: pkg.packageJson.name + (pkg.packageJson.private === true ? " (private)" : ""),
2428
+ value: pkg.packageJson.name,
2429
+ description: changed.includes(pkg.packageJson.name) ? "changed" : void 0
2430
+ }))
2431
+ }))).toSorted((a, b) => a.localeCompare(b));
2432
+ const majorPicked = yield* ask(Prompt.MultiSelect({
2433
+ message: "Which packages should have a major (X.0.0) bump?",
2434
+ choices: chosen.map((name) => ({
2435
+ title: label(name),
2436
+ value: name
2437
+ }))
2438
+ }));
2439
+ const majors = yield* Effect.filter(majorPicked, (name) => {
2440
+ const pkg = byName.get(name);
2441
+ return pkg === void 0 ? Effect.succeed(false) : confirmMajor(pkg);
2442
+ });
2443
+ const afterMajor = chosen.filter((name) => !majors.includes(name));
2444
+ const minors = afterMajor.length === 0 ? [] : yield* ask(Prompt.MultiSelect({
2445
+ message: "Which packages should have a minor (0.X.0) bump?",
2446
+ choices: afterMajor.map((name) => ({
2447
+ title: label(name),
2448
+ value: name
2449
+ }))
2450
+ }));
2451
+ const patches = afterMajor.filter((name) => !minors.includes(name));
2452
+ if (patches.length > 0) yield* success(`The following packages will be patch (0.0.X) bumped:\n${patches.map(label).join(", ")}`);
2453
+ return [
2454
+ ...majors.map((name) => ({
2455
+ name,
2456
+ type: "major"
2457
+ })),
2458
+ ...minors.map((name) => ({
2459
+ name,
2460
+ type: "minor"
2461
+ })),
2462
+ ...patches.map((name) => ({
2463
+ name,
2464
+ type: "patch"
2465
+ }))
2466
+ ];
2467
+ });
2468
+ const changedNames = (changesets, project, since) => changesets.changedPackages(project, Option.getOrUndefined(since)).pipe(Effect.map((packages) => packages.map((pkg) => pkg.packageJson.name)), Effect.catch((error) => Effect.as(warn(`Failed to identify which packages have changed since the ${Option.isSome(since) ? "ref" : "base branch"} due to an error:\n${error.message}`), [])));
2469
+ const add = Effect.fn("add")(function* (options) {
2470
+ const changesets = yield* Changesets;
2471
+ const project = yield* changesets.load(yield* cwd);
2472
+ yield* changesets.refuseUnsupported(project);
2473
+ const versionable = project.packages.packages.filter((pkg) => !shouldSkipPackage(pkg, skipOptions(project.config)));
2474
+ if (project.packages.packages.length === 0 || versionable.length === 0) return yield* new CommandExit({
2475
+ code: 1,
2476
+ message: "No versionable packages found\n Ensure the packages to version are not ignored by the config\n Ensure that relevant package.json files have a `version` field"
2477
+ });
2478
+ const fromFlags = options.major.length + options.minor.length + options.patch.length > 0;
2479
+ const releases = options.empty ? [] : fromFlags ? yield* releasesFromFlags(options, versionable) : yield* pickReleases(yield* changedNames(changesets, project, options.since), versionable);
2480
+ const summary = Option.isSome(options.message) ? options.message.value : options.empty ? "" : yield* askSummary;
2481
+ if (!options.empty) {
2482
+ const summaryLines = [
2483
+ "major",
2484
+ "minor",
2485
+ "patch"
2486
+ ].flatMap((type) => {
2487
+ const names = releases.filter((r) => r.type === type).map((r) => r.name);
2488
+ return names.length > 0 ? [`${type}: ${names.join(", ")}`] : [];
2489
+ });
2490
+ yield* success(["Summary of changesets:", ...summaryLines].join("\n"));
2491
+ }
2492
+ const { file } = yield* changesets.writeChangeset(project, {
2493
+ releases,
2494
+ summary
2495
+ });
2496
+ const relative = (yield* Path.Path).relative(yield* cwd, file);
2497
+ if (releases.some((r) => r.type === "major")) yield* warn("This Changeset includes a major change and we STRONGLY recommend adding more information to the changeset:\n WHAT the breaking change is\n WHY the change was made\n HOW a consumer should update their code");
2498
+ yield* success(`${options.empty ? "Empty " : ""}Changeset added - you can now commit it!\n${relative}`);
2499
+ if (options.open) yield* openInEditor(relative);
2500
+ });
2501
+ const printStatus = (releases, verbose) => {
2502
+ const sorted = releases.toSorted((a, b) => a.name.localeCompare(b.name));
2503
+ const groups = [
2504
+ "major",
2505
+ "minor",
2506
+ "patch"
2507
+ ].flatMap((type) => {
2508
+ const group = sorted.filter((r) => r.type === type);
2509
+ if (group.length === 0) return [];
2510
+ return [`- ${type}`, ...group.flatMap((r) => [` - ${r.name}${verbose ? ` -> ${r.newVersion}` : ""}`, ...verbose ? r.changesets.map((id) => ` - .changeset/${id}.md`) : []])];
2511
+ });
2512
+ return Console.log(`šŸ¦‹ info Packages to be bumped:\n${groups.join("\n")}`.trim());
2513
+ };
2514
+ const unsupported = (name) => Command.make(name, {}, () => Effect.fail(new UnsupportedFeatureError({
2515
+ feature: name,
2516
+ message: `\`changeset ${name}\` is not part of this changesets implementation. Only add, version and status are.`
2517
+ }))).pipe(Command.withDescription(`Not supported (use @changesets/cli for ${name})`));
2518
+ const addCommand = Command.make("add", addFlags, add).pipe(Command.withDescription("Add a new changeset (default)"));
2519
+ const versionCommand = Command.make("version", {
2520
+ ignore: listFlag("ignore", "Packages to ignore"),
2521
+ snapshot: Flag.String("snapshot").pipe(Flag.withDescription("Not supported"), Flag.optional)
2522
+ }, (options) => Effect.gen(function* () {
2523
+ if (Option.isSome(options.snapshot)) return yield* new UnsupportedFeatureError({
2524
+ feature: "snapshot",
2525
+ message: "Snapshot releases are not supported."
2526
+ });
2527
+ yield* (yield* Changesets).version({
2528
+ cwd: yield* cwd,
2529
+ ignore: options.ignore
2530
+ });
2531
+ yield* success("All files have been updated. Review them and commit at your leisure");
2532
+ })).pipe(Command.withDescription("Version packages and create changelogs"));
2533
+ const statusCommand = Command.make("status", {
2534
+ since: Flag.String("since").pipe(Flag.withDescription("Show changesets since the provided git ref"), Flag.optional),
2535
+ verbose: Flag.Boolean("verbose").pipe(Flag.withAlias("v"), Flag.withDescription("Show more information about the changesets"), Flag.withDefault(false)),
2536
+ output: Flag.String("output").pipe(Flag.withAlias("o"), Flag.withDescription("Output the status as JSON to a file"), Flag.withFallbackConfig(Config.String("CHANGESETS_OUTPUT")), Flag.optional)
2537
+ }, (options) => Effect.gen(function* () {
2538
+ const plan = yield* (yield* Changesets).status({
2539
+ cwd: yield* cwd,
2540
+ since: Option.getOrUndefined(options.since),
2541
+ output: Option.getOrUndefined(options.output)
2542
+ });
2543
+ if (Option.isNone(options.output)) yield* printStatus(plan.releases, options.verbose);
2544
+ })).pipe(Command.withDescription("Show the changesets that currently exist"));
2545
+ const changesetCommand = Command.make("changeset", addFlags, add).pipe(Command.withDescription("Manage versioning and changelogs with changesets"), Command.withSubcommands([
2546
+ addCommand,
2547
+ versionCommand,
2548
+ statusCommand,
2549
+ ...[
2550
+ "init",
2551
+ "publish",
2552
+ "publish-plan",
2553
+ "pack",
2554
+ "pre",
2555
+ "git-tag",
2556
+ "tag"
2557
+ ].map(unsupported)
2558
+ ]));
2559
+ const ownError = (error) => Predicate.hasProperty(error, "_tag") && typeof error._tag === "string" && (error._tag.startsWith("@maple-dev/changesets/") || error._tag === "PlatformError") && Predicate.hasProperty(error, "message") && typeof error.message === "string";
2560
+ /** Runs the CLI; expected failures print their message once and exit non-zero. */
2561
+ const main = Command.run(changesetCommand, { version }).pipe(Effect.catch((error) => {
2562
+ if (!ownError(error)) return Effect.fail(error);
2563
+ if (error._tag === "@maple-dev/changesets/CommandExit" && error.message.startsWith("Canceled")) return Console.log(error.message);
2564
+ return Effect.andThen(Console.error(`šŸ¦‹ error ${error.message.split("\n").join("\nšŸ¦‹ error ")}`), Effect.fail(error));
2565
+ }));
2566
+ export { parseChangesetFile as a, CommandExit as c, GitError as d, JsonEditError as f, WorkspaceError as h, assembleReleasePlan as i, ConfigError as l, UnsupportedFeatureError as m, main as n, ChangelogError as o, ReleasePlanError as p, Changesets as r, ChangesetParseError as s, changesetCommand as t, FormatterError as u };