versionary 0.2.0 → 0.4.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.
Files changed (73) hide show
  1. package/README.md +231 -12
  2. package/dist/app/release/artifact-rules.d.ts +3 -0
  3. package/dist/app/release/artifact-rules.js +186 -0
  4. package/dist/app/release/pr.d.ts +23 -0
  5. package/dist/{simple → app/release}/pr.js +90 -79
  6. package/dist/app/release/recovery.d.ts +20 -0
  7. package/dist/app/release/recovery.js +101 -0
  8. package/dist/app/release/release.d.ts +19 -0
  9. package/dist/app/release/release.js +111 -0
  10. package/dist/app/release/state.d.ts +9 -0
  11. package/dist/app/release/state.js +93 -0
  12. package/dist/app/release/verify.d.ts +2 -0
  13. package/dist/app/release/verify.js +5 -0
  14. package/dist/cli/index.js +71 -15
  15. package/dist/config/load-config.js +2 -12
  16. package/dist/config/schema.d.ts +1 -0
  17. package/dist/config/schema.js +34 -1
  18. package/dist/domain/release/changelog.d.ts +12 -0
  19. package/dist/domain/release/changelog.js +123 -0
  20. package/dist/{simple → domain/release}/plan.d.ts +4 -3
  21. package/dist/{simple → domain/release}/plan.js +32 -15
  22. package/dist/domain/release/semver.d.ts +15 -0
  23. package/dist/domain/release/semver.js +111 -0
  24. package/dist/domain/strategy/node.d.ts +2 -0
  25. package/dist/domain/strategy/node.js +67 -0
  26. package/dist/domain/strategy/r.d.ts +2 -0
  27. package/dist/domain/strategy/r.js +46 -0
  28. package/dist/domain/strategy/resolve.d.ts +3 -0
  29. package/dist/domain/strategy/resolve.js +19 -0
  30. package/dist/domain/strategy/rust.d.ts +2 -0
  31. package/dist/domain/strategy/rust.js +369 -0
  32. package/dist/domain/strategy/simple.d.ts +2 -0
  33. package/dist/domain/strategy/simple.js +28 -0
  34. package/dist/domain/strategy/types.d.ts +7 -0
  35. package/dist/domain/strategy/types.js +2 -0
  36. package/dist/index.d.ts +5 -3
  37. package/dist/index.js +5 -1
  38. package/dist/infra/git/commits.d.ts +65 -0
  39. package/dist/infra/git/commits.js +436 -0
  40. package/dist/infra/scm/github/plugin.d.ts +1 -0
  41. package/dist/infra/scm/github/plugin.js +5 -0
  42. package/dist/infra/scm/runtime.d.ts +2 -0
  43. package/dist/infra/scm/runtime.js +24 -0
  44. package/dist/infra/scm/types.d.ts +1 -0
  45. package/dist/infra/scm/types.js +2 -0
  46. package/dist/plugins/runtime.d.ts +1 -2
  47. package/dist/plugins/runtime.js +3 -22
  48. package/dist/scm/github-plugin.js +133 -40
  49. package/dist/strategies/node.d.ts +1 -0
  50. package/dist/strategies/node.js +5 -0
  51. package/dist/strategies/resolve.d.ts +1 -0
  52. package/dist/strategies/resolve.js +5 -0
  53. package/dist/strategies/simple.d.ts +1 -0
  54. package/dist/strategies/simple.js +5 -0
  55. package/dist/strategies/types.d.ts +1 -0
  56. package/dist/strategies/types.js +2 -0
  57. package/dist/types/config.d.ts +1 -0
  58. package/dist/types/plugins.d.ts +1 -0
  59. package/dist/verify/verify-project.js +3 -1
  60. package/package.json +3 -2
  61. package/dist/simple/changelog.d.ts +0 -3
  62. package/dist/simple/changelog.js +0 -46
  63. package/dist/simple/git.d.ts +0 -10
  64. package/dist/simple/git.js +0 -114
  65. package/dist/simple/pr.d.ts +0 -15
  66. package/dist/simple/release.d.ts +0 -1
  67. package/dist/simple/release.js +0 -77
  68. package/dist/simple/semver.d.ts +0 -8
  69. package/dist/simple/semver.js +0 -25
  70. package/dist/simple/state.d.ts +0 -3
  71. package/dist/simple/state.js +0 -47
  72. /package/dist/{simple → infra/git}/repo-url.d.ts +0 -0
  73. /package/dist/{simple → infra/git}/repo-url.js +0 -0
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.nodeVersionStrategy = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function readJsonFile(targetPath) {
10
+ return JSON.parse(node_fs_1.default.readFileSync(targetPath, "utf8"));
11
+ }
12
+ function writeJsonFile(targetPath, value) {
13
+ node_fs_1.default.writeFileSync(targetPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
14
+ }
15
+ function updateNodeLockfileVersion(cwd, version) {
16
+ const updated = [];
17
+ for (const lockfile of ["package-lock.json", "npm-shrinkwrap.json"]) {
18
+ const lockfilePath = node_path_1.default.join(cwd, lockfile);
19
+ if (!node_fs_1.default.existsSync(lockfilePath)) {
20
+ continue;
21
+ }
22
+ const parsed = readJsonFile(lockfilePath);
23
+ parsed.version = version;
24
+ if (parsed.packages?.[""]) {
25
+ parsed.packages[""].version = version;
26
+ }
27
+ writeJsonFile(lockfilePath, parsed);
28
+ updated.push(lockfile);
29
+ }
30
+ return updated;
31
+ }
32
+ exports.nodeVersionStrategy = {
33
+ name: "node",
34
+ getVersionFile(config) {
35
+ return config["version-file"] ?? "package.json";
36
+ },
37
+ readVersion(cwd, config) {
38
+ const versionFile = this.getVersionFile(config);
39
+ const versionPath = node_path_1.default.join(cwd, versionFile);
40
+ if (!node_fs_1.default.existsSync(versionPath)) {
41
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
42
+ }
43
+ const packageJson = readJsonFile(versionPath);
44
+ if (!packageJson.version || typeof packageJson.version !== "string") {
45
+ throw new Error(`${versionFile} is missing a valid "version" field required by release-type "node".`);
46
+ }
47
+ return packageJson.version.trim();
48
+ },
49
+ writeVersion(cwd, config, version) {
50
+ const versionFile = this.getVersionFile(config);
51
+ const versionPath = node_path_1.default.join(cwd, versionFile);
52
+ if (!node_fs_1.default.existsSync(versionPath)) {
53
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
54
+ }
55
+ const packageJson = readJsonFile(versionPath);
56
+ if (!packageJson.version || typeof packageJson.version !== "string") {
57
+ throw new Error(`${versionFile} is missing a valid "version" field required by release-type "node".`);
58
+ }
59
+ packageJson.version = version;
60
+ writeJsonFile(versionPath, packageJson);
61
+ const updatedFiles = [
62
+ versionFile,
63
+ ...updateNodeLockfileVersion(cwd, version),
64
+ ];
65
+ return updatedFiles;
66
+ },
67
+ };
@@ -0,0 +1,2 @@
1
+ import type { VersionStrategy } from "./types.js";
2
+ export declare const rVersionStrategy: VersionStrategy;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.rVersionStrategy = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function readDescriptionVersion(content, versionFile) {
10
+ const match = content.match(/^Version:\s*(.+)\s*$/mu);
11
+ if (!match || !match[1]) {
12
+ throw new Error(`${versionFile} is missing a valid "Version:" field required by release-type "r".`);
13
+ }
14
+ return match[1].trim();
15
+ }
16
+ function writeDescriptionVersion(content, versionFile, version) {
17
+ if (!/^Version:\s*/mu.test(content)) {
18
+ throw new Error(`${versionFile} is missing a valid "Version:" field required by release-type "r".`);
19
+ }
20
+ return content.replace(/^Version:\s*.*$/mu, `Version: ${version}`);
21
+ }
22
+ exports.rVersionStrategy = {
23
+ name: "r",
24
+ getVersionFile(config) {
25
+ return config["version-file"] ?? "DESCRIPTION";
26
+ },
27
+ readVersion(cwd, config) {
28
+ const versionFile = this.getVersionFile(config);
29
+ const versionPath = node_path_1.default.join(cwd, versionFile);
30
+ if (!node_fs_1.default.existsSync(versionPath)) {
31
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
32
+ }
33
+ return readDescriptionVersion(node_fs_1.default.readFileSync(versionPath, "utf8"), versionFile);
34
+ },
35
+ writeVersion(cwd, config, version) {
36
+ const versionFile = this.getVersionFile(config);
37
+ const versionPath = node_path_1.default.join(cwd, versionFile);
38
+ if (!node_fs_1.default.existsSync(versionPath)) {
39
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
40
+ }
41
+ const existing = node_fs_1.default.readFileSync(versionPath, "utf8");
42
+ const updated = writeDescriptionVersion(existing, versionFile, version);
43
+ node_fs_1.default.writeFileSync(versionPath, updated, "utf8");
44
+ return [versionFile];
45
+ },
46
+ };
@@ -0,0 +1,3 @@
1
+ import type { VersionaryConfig } from "../../types/config.js";
2
+ import type { VersionStrategy } from "./types.js";
3
+ export declare function resolveVersionStrategy(config: VersionaryConfig): VersionStrategy;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveVersionStrategy = resolveVersionStrategy;
4
+ const node_js_1 = require("./node.js");
5
+ const r_js_1 = require("./r.js");
6
+ const rust_js_1 = require("./rust.js");
7
+ const simple_js_1 = require("./simple.js");
8
+ function resolveVersionStrategy(config) {
9
+ if (config["release-type"] === "node") {
10
+ return node_js_1.nodeVersionStrategy;
11
+ }
12
+ if (config["release-type"] === "rust") {
13
+ return rust_js_1.rustVersionStrategy;
14
+ }
15
+ if (config["release-type"] === "r") {
16
+ return r_js_1.rVersionStrategy;
17
+ }
18
+ return simple_js_1.simpleVersionStrategy;
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { VersionStrategy } from "./types.js";
2
+ export declare const rustVersionStrategy: VersionStrategy;
@@ -0,0 +1,369 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.rustVersionStrategy = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const toml_1 = __importDefault(require("@iarna/toml"));
10
+ const ROOT_DEPENDENCY_SECTIONS = new Set([
11
+ "dependencies",
12
+ "dev-dependencies",
13
+ "build-dependencies",
14
+ ]);
15
+ function normalizeSlashPath(input) {
16
+ return input.replaceAll("\\", "/");
17
+ }
18
+ function hasGlobPattern(pattern) {
19
+ return /[*?]/u.test(pattern);
20
+ }
21
+ function escapeRegex(input) {
22
+ return input.replace(/[.+^${}()|\\]/gu, "\\$&");
23
+ }
24
+ function globToRegex(pattern) {
25
+ const normalized = normalizeSlashPath(pattern);
26
+ let source = "^";
27
+ for (let index = 0; index < normalized.length; index += 1) {
28
+ const current = normalized[index] ?? "";
29
+ const next = normalized[index + 1] ?? "";
30
+ if (current === "*" && next === "*") {
31
+ source += ".*";
32
+ index += 1;
33
+ continue;
34
+ }
35
+ if (current === "*") {
36
+ source += "[^/]*";
37
+ continue;
38
+ }
39
+ if (current === "?") {
40
+ source += "[^/]";
41
+ continue;
42
+ }
43
+ source += escapeRegex(current);
44
+ }
45
+ source += "$";
46
+ return new RegExp(source, "u");
47
+ }
48
+ function findCargoManifestDirectories(rootDir) {
49
+ const results = new Set();
50
+ const queue = [rootDir];
51
+ while (queue.length > 0) {
52
+ const currentDir = queue.shift();
53
+ if (!currentDir) {
54
+ continue;
55
+ }
56
+ const entries = node_fs_1.default.readdirSync(currentDir, {
57
+ withFileTypes: true,
58
+ });
59
+ let hasCargoManifest = false;
60
+ for (const entry of entries) {
61
+ const fullPath = node_path_1.default.join(currentDir, entry.name);
62
+ if (entry.isFile() && entry.name === "Cargo.toml") {
63
+ hasCargoManifest = true;
64
+ continue;
65
+ }
66
+ if (entry.isDirectory()) {
67
+ queue.push(fullPath);
68
+ }
69
+ }
70
+ if (hasCargoManifest) {
71
+ const rel = normalizeSlashPath(node_path_1.default.relative(rootDir, currentDir));
72
+ results.add(rel === "" ? "." : rel);
73
+ }
74
+ }
75
+ return [...results].sort((a, b) => a.localeCompare(b));
76
+ }
77
+ function parseCargoManifest(versionFile, cargoTomlRaw) {
78
+ let parsed;
79
+ try {
80
+ parsed = toml_1.default.parse(cargoTomlRaw);
81
+ }
82
+ catch (error) {
83
+ const message = error instanceof Error ? error.message : String(error);
84
+ throw new Error(`Failed to parse ${versionFile}: ${message}`);
85
+ }
86
+ const parsedRecord = parsed;
87
+ const packageTable = parsedRecord.package && typeof parsedRecord.package === "object"
88
+ ? parsedRecord.package
89
+ : null;
90
+ const workspaceTable = parsedRecord.workspace && typeof parsedRecord.workspace === "object"
91
+ ? parsedRecord.workspace
92
+ : null;
93
+ return { packageTable, workspaceTable };
94
+ }
95
+ function isCrateManifest(versionFile, cargoTomlRaw) {
96
+ const parsed = parseCargoManifest(versionFile, cargoTomlRaw);
97
+ return parsed.packageTable !== null;
98
+ }
99
+ function readWorkspaceMemberPatterns(workspaceTable) {
100
+ if (!workspaceTable) {
101
+ return [];
102
+ }
103
+ const members = workspaceTable.members;
104
+ if (!Array.isArray(members)) {
105
+ return [];
106
+ }
107
+ return members
108
+ .filter((member) => typeof member === "string")
109
+ .map((member) => normalizeSlashPath(member.trim()))
110
+ .filter((member) => member.length > 0);
111
+ }
112
+ function readWorkspaceExcludes(workspaceTable) {
113
+ if (!workspaceTable) {
114
+ return [];
115
+ }
116
+ const excludes = workspaceTable.exclude;
117
+ if (!Array.isArray(excludes)) {
118
+ return [];
119
+ }
120
+ return excludes
121
+ .filter((entry) => typeof entry === "string")
122
+ .map((entry) => normalizeSlashPath(entry.trim()))
123
+ .filter((entry) => entry.length > 0);
124
+ }
125
+ function resolveWorkspaceMemberManifests(rootDir, workspaceTable) {
126
+ const memberPatterns = readWorkspaceMemberPatterns(workspaceTable);
127
+ if (memberPatterns.length === 0) {
128
+ return [];
129
+ }
130
+ const excludes = readWorkspaceExcludes(workspaceTable);
131
+ const excludeRegexes = excludes.map((pattern) => globToRegex(pattern));
132
+ const cargoDirs = findCargoManifestDirectories(rootDir);
133
+ const manifests = new Set();
134
+ for (const pattern of memberPatterns) {
135
+ if (hasGlobPattern(pattern)) {
136
+ const regex = globToRegex(pattern);
137
+ for (const dir of cargoDirs) {
138
+ if (regex.test(dir)) {
139
+ manifests.add(normalizeSlashPath(node_path_1.default.posix.join(dir, "Cargo.toml")));
140
+ }
141
+ }
142
+ continue;
143
+ }
144
+ const candidateDir = normalizeSlashPath(pattern);
145
+ if (excludeRegexes.some((regex) => regex.test(candidateDir))) {
146
+ continue;
147
+ }
148
+ const cargoPath = node_path_1.default.join(rootDir, candidateDir, "Cargo.toml");
149
+ if (node_fs_1.default.existsSync(cargoPath)) {
150
+ manifests.add(normalizeSlashPath(node_path_1.default.posix.join(candidateDir, "Cargo.toml")));
151
+ }
152
+ }
153
+ const filtered = [...manifests].filter((manifestPath) => {
154
+ const dir = normalizeSlashPath(node_path_1.default.posix.dirname(manifestPath));
155
+ return !excludeRegexes.some((regex) => regex.test(dir));
156
+ });
157
+ return filtered.sort((a, b) => a.localeCompare(b));
158
+ }
159
+ function collectRustTargetManifests(cwd, versionFile) {
160
+ if (node_path_1.default.basename(versionFile) !== "Cargo.toml") {
161
+ throw new Error(`Rust strategy requires "version-file" to point to a Cargo.toml manifest (received "${versionFile}").`);
162
+ }
163
+ const rootManifestPath = node_path_1.default.join(cwd, versionFile);
164
+ if (!node_fs_1.default.existsSync(rootManifestPath)) {
165
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
166
+ }
167
+ const rootRaw = node_fs_1.default.readFileSync(rootManifestPath, "utf8");
168
+ const parsedRoot = parseCargoManifest(versionFile, rootRaw);
169
+ const rootIsCrate = parsedRoot.packageTable !== null;
170
+ const rootDir = node_path_1.default.dirname(rootManifestPath);
171
+ const workspaceMembers = resolveWorkspaceMemberManifests(rootDir, parsedRoot.workspaceTable);
172
+ if (rootIsCrate) {
173
+ const relRoot = normalizeSlashPath(node_path_1.default.relative(cwd, rootManifestPath));
174
+ return [...new Set([relRoot, ...workspaceMembers])].sort((a, b) => a.localeCompare(b));
175
+ }
176
+ if (workspaceMembers.length > 0) {
177
+ return workspaceMembers;
178
+ }
179
+ throw new Error(`Configured rust target "${versionFile}" is not a Rust crate manifest. Expected [package].version or [workspace].members with crate Cargo.toml files.`);
180
+ }
181
+ function readCargoVersion(cargoTomlRaw, versionFile) {
182
+ const { packageTable } = parseCargoManifest(versionFile, cargoTomlRaw);
183
+ if (!packageTable || typeof packageTable !== "object") {
184
+ throw new Error(`${versionFile} is missing [package].version. Add [package] with a SemVer version.`);
185
+ }
186
+ const rawVersion = packageTable.version;
187
+ if (rawVersion === undefined) {
188
+ throw new Error(`${versionFile} is missing [package].version. Add [package] with a SemVer version.`);
189
+ }
190
+ if (typeof rawVersion !== "string" || rawVersion.trim().length === 0) {
191
+ throw new Error(`${versionFile} has invalid [package].version. Expected a non-empty SemVer string.`);
192
+ }
193
+ return rawVersion.trim();
194
+ }
195
+ function readCargoPackageName(cargoTomlRaw, versionFile) {
196
+ const { packageTable } = parseCargoManifest(versionFile, cargoTomlRaw);
197
+ if (!packageTable || typeof packageTable !== "object") {
198
+ throw new Error(`${versionFile} is missing [package].name. Add [package] with a crate name.`);
199
+ }
200
+ const rawName = packageTable.name;
201
+ if (typeof rawName !== "string" || rawName.trim().length === 0) {
202
+ throw new Error(`${versionFile} has invalid [package].name. Expected a non-empty crate name.`);
203
+ }
204
+ return rawName.trim();
205
+ }
206
+ function writeCargoVersion(cargoTomlRaw, versionFile, version) {
207
+ const lineEnding = cargoTomlRaw.includes("\r\n") ? "\r\n" : "\n";
208
+ const hasFinalLineEnding = cargoTomlRaw.endsWith("\n") || cargoTomlRaw.endsWith("\r\n");
209
+ const lines = cargoTomlRaw.split(/\r?\n/u);
210
+ let inPackageSection = false;
211
+ let foundPackageSection = false;
212
+ let replacedVersion = false;
213
+ for (let index = 0; index < lines.length; index += 1) {
214
+ const line = lines[index] ?? "";
215
+ const sectionMatch = line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/u);
216
+ if (sectionMatch) {
217
+ const section = sectionMatch[1]?.trim();
218
+ inPackageSection = section === "package";
219
+ if (inPackageSection) {
220
+ foundPackageSection = true;
221
+ }
222
+ continue;
223
+ }
224
+ if (!inPackageSection) {
225
+ continue;
226
+ }
227
+ const versionMatch = line.match(/^(\s*version\s*=\s*)(["'])([^"']*)(\2)(\s*(?:#.*)?)?$/u);
228
+ if (!versionMatch) {
229
+ continue;
230
+ }
231
+ const [, prefix = "", quote = '"', , , suffix = ""] = versionMatch;
232
+ lines[index] = `${prefix}${quote}${version}${quote}${suffix}`;
233
+ replacedVersion = true;
234
+ break;
235
+ }
236
+ if (!foundPackageSection) {
237
+ throw new Error(`${versionFile} is missing [package].version. Add [package] with a SemVer version.`);
238
+ }
239
+ if (!replacedVersion) {
240
+ throw new Error(`${versionFile} is missing [package].version. Add [package] with a SemVer version.`);
241
+ }
242
+ let updated = lines.join(lineEnding);
243
+ if (hasFinalLineEnding && !updated.endsWith(lineEnding)) {
244
+ updated += lineEnding;
245
+ }
246
+ if (!hasFinalLineEnding && updated.endsWith(lineEnding)) {
247
+ updated = updated.slice(0, -lineEnding.length);
248
+ }
249
+ return updated;
250
+ }
251
+ function isDependencySection(section) {
252
+ if (ROOT_DEPENDENCY_SECTIONS.has(section)) {
253
+ return true;
254
+ }
255
+ if (!section.startsWith("target.")) {
256
+ return false;
257
+ }
258
+ return (section.endsWith(".dependencies") ||
259
+ section.endsWith(".dev-dependencies") ||
260
+ section.endsWith(".build-dependencies"));
261
+ }
262
+ function parseDependencyName(line) {
263
+ const match = line.match(/^\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_-]+))\s*=/u);
264
+ return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
265
+ }
266
+ function writeInternalDependencyVersionInLine(line, dependencyName, internalCrates, version) {
267
+ if (!internalCrates.has(dependencyName)) {
268
+ return line;
269
+ }
270
+ const stringVersionMatch = line.match(/^(\s*(?:"[^"]+"|'[^']+'|[A-Za-z0-9_-]+)\s*=\s*)(["'])([^"']*)(\2)(\s*(?:#.*)?)?$/u);
271
+ if (stringVersionMatch) {
272
+ const [, prefix = "", quote = '"', , , suffix = ""] = stringVersionMatch;
273
+ return `${prefix}${quote}${version}${quote}${suffix}`;
274
+ }
275
+ const inlineTableMatch = line.match(/^(\s*(?:"[^"]+"|'[^']+'|[A-Za-z0-9_-]+)\s*=\s*\{)(.*)(\}\s*(?:#.*)?)$/u);
276
+ if (!inlineTableMatch) {
277
+ return line;
278
+ }
279
+ const [, prefix = "", tableBody = "", suffix = ""] = inlineTableMatch;
280
+ const updatedTableBody = tableBody.replace(/(\bversion\s*=\s*)(["'])([^"']*)(\2)/u, `$1$2${version}$4`);
281
+ if (updatedTableBody === tableBody) {
282
+ return line;
283
+ }
284
+ return `${prefix}${updatedTableBody}${suffix}`;
285
+ }
286
+ function writeInternalDependencyVersions(cargoTomlRaw, internalCrates, version) {
287
+ if (internalCrates.size === 0) {
288
+ return cargoTomlRaw;
289
+ }
290
+ const lineEnding = cargoTomlRaw.includes("\r\n") ? "\r\n" : "\n";
291
+ const hasFinalLineEnding = cargoTomlRaw.endsWith("\n") || cargoTomlRaw.endsWith("\r\n");
292
+ const lines = cargoTomlRaw.split(/\r?\n/u);
293
+ let currentSection = null;
294
+ for (let index = 0; index < lines.length; index += 1) {
295
+ const line = lines[index] ?? "";
296
+ const sectionMatch = line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/u);
297
+ if (sectionMatch) {
298
+ currentSection = sectionMatch[1]?.trim() ?? null;
299
+ continue;
300
+ }
301
+ if (!currentSection || !isDependencySection(currentSection)) {
302
+ continue;
303
+ }
304
+ const dependencyName = parseDependencyName(line);
305
+ if (!dependencyName) {
306
+ continue;
307
+ }
308
+ lines[index] = writeInternalDependencyVersionInLine(line, dependencyName, internalCrates, version);
309
+ }
310
+ let updated = lines.join(lineEnding);
311
+ if (hasFinalLineEnding && !updated.endsWith(lineEnding)) {
312
+ updated += lineEnding;
313
+ }
314
+ if (!hasFinalLineEnding && updated.endsWith(lineEnding)) {
315
+ updated = updated.slice(0, -lineEnding.length);
316
+ }
317
+ return updated;
318
+ }
319
+ exports.rustVersionStrategy = {
320
+ name: "rust",
321
+ getVersionFile(config) {
322
+ return config["version-file"] ?? "Cargo.toml";
323
+ },
324
+ readVersion(cwd, config) {
325
+ const versionFile = this.getVersionFile(config);
326
+ const manifests = collectRustTargetManifests(cwd, versionFile);
327
+ const selectedManifest = manifests[0];
328
+ if (!selectedManifest) {
329
+ throw new Error(`Configured rust target "${versionFile}" did not resolve to a Rust crate manifest.`);
330
+ }
331
+ const cargoTomlRaw = node_fs_1.default.readFileSync(node_path_1.default.join(cwd, selectedManifest), "utf8");
332
+ return readCargoVersion(cargoTomlRaw, selectedManifest);
333
+ },
334
+ writeVersion(cwd, config, version) {
335
+ const versionFile = this.getVersionFile(config);
336
+ const manifests = collectRustTargetManifests(cwd, versionFile);
337
+ const updatedFiles = [];
338
+ const internalCrates = new Set();
339
+ for (const manifest of manifests) {
340
+ const versionPath = node_path_1.default.join(cwd, manifest);
341
+ if (!node_fs_1.default.existsSync(versionPath)) {
342
+ continue;
343
+ }
344
+ const cargoTomlRaw = node_fs_1.default.readFileSync(versionPath, "utf8");
345
+ if (!isCrateManifest(manifest, cargoTomlRaw)) {
346
+ continue;
347
+ }
348
+ internalCrates.add(readCargoPackageName(cargoTomlRaw, manifest));
349
+ }
350
+ for (const manifest of manifests) {
351
+ const versionPath = node_path_1.default.join(cwd, manifest);
352
+ if (!node_fs_1.default.existsSync(versionPath)) {
353
+ continue;
354
+ }
355
+ const cargoTomlRaw = node_fs_1.default.readFileSync(versionPath, "utf8");
356
+ if (!isCrateManifest(manifest, cargoTomlRaw)) {
357
+ continue;
358
+ }
359
+ readCargoVersion(cargoTomlRaw, manifest);
360
+ const updatedCargoToml = writeInternalDependencyVersions(writeCargoVersion(cargoTomlRaw, manifest, version), internalCrates, version);
361
+ node_fs_1.default.writeFileSync(versionPath, updatedCargoToml, "utf8");
362
+ updatedFiles.push(manifest);
363
+ }
364
+ if (updatedFiles.length === 0) {
365
+ throw new Error(`Configured rust target "${versionFile}" did not resolve to a Rust crate manifest to update.`);
366
+ }
367
+ return updatedFiles.sort((a, b) => a.localeCompare(b));
368
+ },
369
+ };
@@ -0,0 +1,2 @@
1
+ import type { VersionStrategy } from "./types.js";
2
+ export declare const simpleVersionStrategy: VersionStrategy;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.simpleVersionStrategy = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ exports.simpleVersionStrategy = {
10
+ name: "simple",
11
+ getVersionFile(config) {
12
+ return config["version-file"] ?? "version.txt";
13
+ },
14
+ readVersion(cwd, config) {
15
+ const versionFile = this.getVersionFile(config);
16
+ const versionPath = node_path_1.default.join(cwd, versionFile);
17
+ if (!node_fs_1.default.existsSync(versionPath)) {
18
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
19
+ }
20
+ return node_fs_1.default.readFileSync(versionPath, "utf8").trim();
21
+ },
22
+ writeVersion(cwd, config, version) {
23
+ const versionFile = this.getVersionFile(config);
24
+ const versionPath = node_path_1.default.join(cwd, versionFile);
25
+ node_fs_1.default.writeFileSync(versionPath, `${version}\n`, "utf8");
26
+ return [versionFile];
27
+ },
28
+ };
@@ -0,0 +1,7 @@
1
+ import type { VersionaryConfig } from "../../types/config.js";
2
+ export interface VersionStrategy {
3
+ name: string;
4
+ getVersionFile(config: VersionaryConfig): string;
5
+ readVersion(cwd: string, config: VersionaryConfig): string;
6
+ writeVersion(cwd: string, config: VersionaryConfig, version: string): string[];
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- export type { VersionaryConfig, VersionaryPackage, VersionaryArtifactRule, } from "./types/config.js";
2
- export type { VersionaryPluginCapability, VersionaryPluginContext, VersionaryPluginRuntime, VersionaryScmReviewRequestInput, VersionaryScmReviewRequestResult, VersionaryScmReleaseMetadataInput, VersionaryScmReleaseMetadataResult, } from "./types/plugins.js";
3
1
  export { loadConfig } from "./config/load-config.js";
4
- export { findPluginsByCapability, pluginHasCapability } from "./plugins/capabilities.js";
2
+ export { resolveVersionStrategy } from "./domain/strategy/resolve.js";
3
+ export { createGitHubPlugin } from "./infra/scm/github/plugin.js";
4
+ export { findPluginsByCapability, pluginHasCapability, } from "./plugins/capabilities.js";
5
5
  export { loadRuntimePlugins } from "./plugins/runtime.js";
6
+ export type { VersionaryArtifactRule, VersionaryConfig, VersionaryPackage, } from "./types/config.js";
7
+ export type { VersionaryPluginCapability, VersionaryPluginContext, VersionaryPluginRuntime, VersionaryScmReleaseMetadataInput, VersionaryScmReleaseMetadataResult, VersionaryScmReviewRequestInput, VersionaryScmReviewRequestResult, } from "./types/plugins.js";
6
8
  export { verifyProject } from "./verify/verify-project.js";
package/dist/index.js CHANGED
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifyProject = exports.loadRuntimePlugins = exports.pluginHasCapability = exports.findPluginsByCapability = exports.loadConfig = void 0;
3
+ exports.verifyProject = exports.loadRuntimePlugins = exports.pluginHasCapability = exports.findPluginsByCapability = exports.createGitHubPlugin = exports.resolveVersionStrategy = exports.loadConfig = void 0;
4
4
  var load_config_js_1 = require("./config/load-config.js");
5
5
  Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return load_config_js_1.loadConfig; } });
6
+ var resolve_js_1 = require("./domain/strategy/resolve.js");
7
+ Object.defineProperty(exports, "resolveVersionStrategy", { enumerable: true, get: function () { return resolve_js_1.resolveVersionStrategy; } });
8
+ var plugin_js_1 = require("./infra/scm/github/plugin.js");
9
+ Object.defineProperty(exports, "createGitHubPlugin", { enumerable: true, get: function () { return plugin_js_1.createGitHubPlugin; } });
6
10
  var capabilities_js_1 = require("./plugins/capabilities.js");
7
11
  Object.defineProperty(exports, "findPluginsByCapability", { enumerable: true, get: function () { return capabilities_js_1.findPluginsByCapability; } });
8
12
  Object.defineProperty(exports, "pluginHasCapability", { enumerable: true, get: function () { return capabilities_js_1.pluginHasCapability; } });
@@ -0,0 +1,65 @@
1
+ import type { ReleaseType } from "../../domain/release/semver.js";
2
+ export interface CommitInfo {
3
+ hash: string;
4
+ subject: string;
5
+ }
6
+ export interface ParsedCommit {
7
+ hash: string;
8
+ subject: string;
9
+ body: string;
10
+ fullMessage: string;
11
+ type: string | null;
12
+ scope: string | null;
13
+ description: string;
14
+ isBreaking: boolean;
15
+ isRevert: boolean;
16
+ footers: Array<{
17
+ token: string;
18
+ value: string;
19
+ }>;
20
+ revertedShas: string[];
21
+ header?: string;
22
+ merge?: string | null;
23
+ footer?: string | null;
24
+ notes?: CommitNote[];
25
+ references?: CommitReference[];
26
+ mentions?: string[];
27
+ revert?: RevertInfo | null;
28
+ isConventional?: boolean;
29
+ diagnostics?: ParseDiagnostic[];
30
+ }
31
+ export type CommitParseDiagnosticCode = "invalid-header" | "malformed-breaking-footer" | "malformed-reference" | "ambiguous-revert";
32
+ export interface ParseDiagnostic {
33
+ code: CommitParseDiagnosticCode;
34
+ message: string;
35
+ }
36
+ export interface CommitNote {
37
+ title: string;
38
+ text: string;
39
+ }
40
+ export interface CommitReference {
41
+ action: string | null;
42
+ owner: string | null;
43
+ repository: string | null;
44
+ issue: string | null;
45
+ raw: string;
46
+ prefix: "#" | "GH-";
47
+ }
48
+ export interface RevertInfo {
49
+ header: string;
50
+ hashes: string[];
51
+ }
52
+ export declare function parseConventionalCommitMessage(subject: string, body?: string): ParsedCommit;
53
+ export declare function parseConventionalCommitMessageDetailed(header: string, body?: string): ParsedCommit;
54
+ export declare function getCommitsSinceLastTag(cwd?: string, baselineSha?: string | null): CommitInfo[];
55
+ export declare function getCommitsForPath(cwd?: string, baselineSha?: string | null, packagePath?: string, excludePaths?: string[]): CommitInfo[];
56
+ export declare function getParsedCommitsSinceLastTag(cwd?: string, baselineSha?: string | null): ParsedCommit[];
57
+ export declare function getParsedCommitsForPath(cwd?: string, baselineSha?: string | null, packagePath?: string, excludePaths?: string[]): ParsedCommit[];
58
+ export declare function inferReleaseTypeFromParsedCommit(commit: ParsedCommit): ReleaseType;
59
+ export declare function inferReleaseTypeFromSubject(subject: string): ReleaseType;
60
+ export declare function isReleasableCommit(subject: string): boolean;
61
+ export declare function getCommitParseDiagnostics(commit: ParsedCommit): ParseDiagnostic[];
62
+ export declare function analyzeCommits(commits: CommitInfo[]): ReleaseType;
63
+ export declare function applyRevertSuppression(commits: ParsedCommit[]): ParsedCommit[];
64
+ export declare function analyzeParsedCommits(commits: ParsedCommit[]): ReleaseType;
65
+ export declare function isReleasableParsedCommit(commit: ParsedCommit): boolean;