@astra-kit/rp 0.0.0-stage → 0.10.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 (125) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +352 -2
  4. package/bin/astra-rp.js +51 -0
  5. package/package.json +93 -4
  6. package/src/build-gate.js +157 -0
  7. package/src/changelog-blocks.js +53 -0
  8. package/src/changelog.js +108 -0
  9. package/src/constants/build-gate.js +30 -0
  10. package/src/constants/changelog-draft.js +180 -0
  11. package/src/constants/changelog.js +74 -0
  12. package/src/constants/ci-release.js +346 -0
  13. package/src/constants/cli.js +12 -0
  14. package/src/constants/create-version.js +523 -0
  15. package/src/constants/guard-publish.js +29 -0
  16. package/src/constants/index.js +18 -0
  17. package/src/constants/monorepo.js +57 -0
  18. package/src/constants/package-manager.js +56 -0
  19. package/src/constants/registry.js +72 -0
  20. package/src/constants/terminal-ui.js +93 -0
  21. package/src/constants/version-files.js +63 -0
  22. package/src/constants/versions.js +60 -0
  23. package/src/create-version/artifact.js +343 -0
  24. package/src/create-version/changelog-draft-workspace.js +124 -0
  25. package/src/create-version/changelog-draft.js +380 -0
  26. package/src/create-version/changelog.js +264 -0
  27. package/src/create-version/ci-config.js +54 -0
  28. package/src/create-version/ci-publication-history.js +113 -0
  29. package/src/create-version/ci-release-identity-step.js +58 -0
  30. package/src/create-version/ci-setup.js +794 -0
  31. package/src/create-version/ci.js +555 -0
  32. package/src/create-version/committed-tree.js +75 -0
  33. package/src/create-version/config.js +489 -0
  34. package/src/create-version/errors.js +46 -0
  35. package/src/create-version/github-workflow.js +199 -0
  36. package/src/create-version/index.js +54 -0
  37. package/src/create-version/json.js +32 -0
  38. package/src/create-version/jsonc.js +76 -0
  39. package/src/create-version/jsr.js +96 -0
  40. package/src/create-version/local-changes.js +178 -0
  41. package/src/create-version/npm-auth.js +142 -0
  42. package/src/create-version/npm.js +812 -0
  43. package/src/create-version/plan.js +1200 -0
  44. package/src/create-version/process.js +334 -0
  45. package/src/create-version/registry-config.js +121 -0
  46. package/src/create-version/registry-http.js +62 -0
  47. package/src/create-version/registry.js +220 -0
  48. package/src/create-version/release-execution.js +45 -0
  49. package/src/create-version/release-tag-config.js +76 -0
  50. package/src/create-version/run.js +2087 -0
  51. package/src/create-version/state.js +461 -0
  52. package/src/create-version/strict-utf8.js +11 -0
  53. package/src/create-version/templates/release.yml +52 -0
  54. package/src/create-version/templates/vercel-ignore-build.mjs +66 -0
  55. package/src/guard-publish.js +77 -0
  56. package/src/index.js +42 -0
  57. package/src/monorepo/plan.js +475 -0
  58. package/src/monorepo/release-commit.js +46 -0
  59. package/src/monorepo/run.js +927 -0
  60. package/src/monorepo/state.js +206 -0
  61. package/src/monorepo/workspaces.js +327 -0
  62. package/src/package-manager.js +107 -0
  63. package/src/terminal-ui.js +548 -0
  64. package/src/testing.js +70 -0
  65. package/src/version-files.js +142 -0
  66. package/src/versions.js +303 -0
  67. package/types/build-gate.d.ts +43 -0
  68. package/types/changelog-blocks.d.ts +33 -0
  69. package/types/changelog.d.ts +61 -0
  70. package/types/constants/build-gate.d.ts +23 -0
  71. package/types/constants/changelog-draft.d.ts +138 -0
  72. package/types/constants/changelog.d.ts +56 -0
  73. package/types/constants/ci-release.d.ts +284 -0
  74. package/types/constants/cli.d.ts +11 -0
  75. package/types/constants/create-version.d.ts +397 -0
  76. package/types/constants/guard-publish.d.ts +25 -0
  77. package/types/constants/index.d.ts +17 -0
  78. package/types/constants/monorepo.d.ts +45 -0
  79. package/types/constants/package-manager.d.ts +40 -0
  80. package/types/constants/registry.d.ts +79 -0
  81. package/types/constants/terminal-ui.d.ts +73 -0
  82. package/types/constants/version-files.d.ts +51 -0
  83. package/types/constants/versions.d.ts +47 -0
  84. package/types/create-version/artifact.d.ts +113 -0
  85. package/types/create-version/changelog-draft-workspace.d.ts +33 -0
  86. package/types/create-version/changelog-draft.d.ts +127 -0
  87. package/types/create-version/changelog.d.ts +141 -0
  88. package/types/create-version/ci-config.d.ts +33 -0
  89. package/types/create-version/ci-publication-history.d.ts +51 -0
  90. package/types/create-version/ci-release-identity-step.d.ts +12 -0
  91. package/types/create-version/ci-setup.d.ts +148 -0
  92. package/types/create-version/ci.d.ts +145 -0
  93. package/types/create-version/committed-tree.d.ts +55 -0
  94. package/types/create-version/config.d.ts +128 -0
  95. package/types/create-version/errors.d.ts +39 -0
  96. package/types/create-version/github-workflow.d.ts +41 -0
  97. package/types/create-version/index.d.ts +28 -0
  98. package/types/create-version/json.d.ts +21 -0
  99. package/types/create-version/jsonc.d.ts +21 -0
  100. package/types/create-version/jsr.d.ts +52 -0
  101. package/types/create-version/local-changes.d.ts +52 -0
  102. package/types/create-version/npm-auth.d.ts +60 -0
  103. package/types/create-version/npm.d.ts +331 -0
  104. package/types/create-version/plan.d.ts +369 -0
  105. package/types/create-version/process.d.ts +151 -0
  106. package/types/create-version/registry-config.d.ts +56 -0
  107. package/types/create-version/registry-http.d.ts +22 -0
  108. package/types/create-version/registry.d.ts +86 -0
  109. package/types/create-version/release-execution.d.ts +22 -0
  110. package/types/create-version/release-tag-config.d.ts +18 -0
  111. package/types/create-version/run.d.ts +407 -0
  112. package/types/create-version/state.d.ts +204 -0
  113. package/types/create-version/strict-utf8.d.ts +10 -0
  114. package/types/guard-publish.d.ts +49 -0
  115. package/types/index.d.ts +23 -0
  116. package/types/monorepo/plan.d.ts +68 -0
  117. package/types/monorepo/release-commit.d.ts +29 -0
  118. package/types/monorepo/run.d.ts +68 -0
  119. package/types/monorepo/state.d.ts +96 -0
  120. package/types/monorepo/workspaces.d.ts +86 -0
  121. package/types/package-manager.d.ts +58 -0
  122. package/types/terminal-ui.d.ts +196 -0
  123. package/types/testing.d.ts +31 -0
  124. package/types/version-files.d.ts +59 -0
  125. package/types/versions.d.ts +158 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * `versionFiles`: files besides `package.json` that carry the version (a CLI
3
+ * `--version`, a user agent, a constant) and are rewritten in the release
4
+ * commit. Only marked lines change:
5
+ *
6
+ * - a line with `astra-rp-version` (or release-please's `x-release-please-version`);
7
+ * - every line between `astra-rp-start-version` and `astra-rp-end` (or
8
+ * `x-release-please-start-version` / `x-release-please-end`).
9
+ *
10
+ * @module version-files
11
+ */
12
+
13
+ import {
14
+ LINE_TERMINATOR_BOUNDARY_PATTERN,
15
+ MARKED_VERSION_PATTERN,
16
+ VERSION_BLOCK_END_MARKERS,
17
+ VERSION_BLOCK_PROBLEM,
18
+ VERSION_BLOCK_START_MARKERS,
19
+ VERSION_LINE_MARKERS,
20
+ } from "./constants/version-files.js";
21
+
22
+ /**
23
+ * @typedef {{ content: string, markedLines: number, replacements: number }} VersionFileUpdate
24
+ * `markedLines` counts the lines the markers selected; `replacements` the versions rewritten.
25
+ */
26
+
27
+ /**
28
+ * @typedef {"unterminated" | "unopened" | "nested" | "mismatched"} VersionBlockProblem
29
+ * How the block markers are paired wrongly; see `VERSION_BLOCK_PROBLEM`.
30
+ */
31
+
32
+ /**
33
+ * Version block markers paired wrongly: nothing is rewritten, since guessing where the block ends
34
+ * could change versions outside the intended section.
35
+ */
36
+ export class VersionBlockError extends Error {
37
+ /**
38
+ * @param {{ problem: VersionBlockProblem, lineNumber: number, marker: string, openingLineNumber?: number, openingMarker?: string }} details
39
+ * `lineNumber` (1-based) and `marker` locate the offending marker; `openingLineNumber` and
40
+ * `openingMarker` the start marker of the block still open, when there is one.
41
+ */
42
+ constructor({ problem, lineNumber, marker, openingLineNumber, openingMarker }) {
43
+ super(`astra-rp:updateVersionMarkers ${problem} version block: ${marker} at line ${lineNumber}`);
44
+ this.name = "VersionBlockError";
45
+ this.problem = problem;
46
+ this.lineNumber = lineNumber;
47
+ this.marker = marker;
48
+ this.openingLineNumber = openingLineNumber;
49
+ this.openingMarker = openingMarker;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * @param {string} line - Line of the file.
55
+ * @param {readonly string[]} markers - Markers to look for.
56
+ * @returns {number} Index of the first marker the line carries, or `-1` when it carries none.
57
+ */
58
+ function findMarkerIndex(line, markers) {
59
+ return markers.findIndex((marker) => line.includes(marker));
60
+ }
61
+
62
+ /**
63
+ * Rewrites the versions of the marked lines of a file, keeping everything else (line endings
64
+ * included) byte for byte. `\n`, `\r\n` and a lone `\r` all end a line.
65
+ *
66
+ * @param {string} content - File content.
67
+ * @param {string} version - Version to write.
68
+ * @returns {VersionFileUpdate} New content and what the markers selected.
69
+ * @throws {VersionBlockError} When a block is left open, closed without being opened, opened
70
+ * inside another block or closed with the end marker of the other tool.
71
+ */
72
+ export function updateVersionMarkers(content, version) {
73
+ /** @type {{ markerIndex: number, lineNumber: number } | null} */
74
+ let openBlock = null;
75
+ let markedLines = 0;
76
+ let replacements = 0;
77
+
78
+ /** @type {string[]} */
79
+ const lines = [];
80
+
81
+ for (const [index, line] of content.split(LINE_TERMINATOR_BOUNDARY_PATTERN).entries()) {
82
+ const lineNumber = index + 1;
83
+ const startMarkerIndex = findMarkerIndex(line, VERSION_BLOCK_START_MARKERS);
84
+ const endMarkerIndex = findMarkerIndex(line, VERSION_BLOCK_END_MARKERS);
85
+
86
+ if (startMarkerIndex !== -1) {
87
+ if (openBlock !== null) {
88
+ throw new VersionBlockError({
89
+ problem: VERSION_BLOCK_PROBLEM.nested,
90
+ lineNumber,
91
+ marker: VERSION_BLOCK_START_MARKERS[startMarkerIndex],
92
+ openingLineNumber: openBlock.lineNumber,
93
+ openingMarker: VERSION_BLOCK_START_MARKERS[openBlock.markerIndex],
94
+ });
95
+ }
96
+ openBlock = { markerIndex: startMarkerIndex, lineNumber };
97
+ lines.push(line);
98
+ continue;
99
+ }
100
+ if (endMarkerIndex !== -1) {
101
+ if (openBlock === null) {
102
+ throw new VersionBlockError({ problem: VERSION_BLOCK_PROBLEM.unopened, lineNumber, marker: VERSION_BLOCK_END_MARKERS[endMarkerIndex] });
103
+ }
104
+ if (endMarkerIndex !== openBlock.markerIndex) {
105
+ throw new VersionBlockError({
106
+ problem: VERSION_BLOCK_PROBLEM.mismatched,
107
+ lineNumber,
108
+ marker: VERSION_BLOCK_END_MARKERS[endMarkerIndex],
109
+ openingLineNumber: openBlock.lineNumber,
110
+ openingMarker: VERSION_BLOCK_START_MARKERS[openBlock.markerIndex],
111
+ });
112
+ }
113
+ openBlock = null;
114
+ lines.push(line);
115
+ continue;
116
+ }
117
+ if (openBlock === null && findMarkerIndex(line, VERSION_LINE_MARKERS) === -1) {
118
+ lines.push(line);
119
+ continue;
120
+ }
121
+ markedLines += 1;
122
+ lines.push(
123
+ line.replace(MARKED_VERSION_PATTERN, () => {
124
+ replacements += 1;
125
+ return version;
126
+ })
127
+ );
128
+ }
129
+
130
+ if (openBlock !== null) {
131
+ const openingMarker = VERSION_BLOCK_START_MARKERS[openBlock.markerIndex];
132
+ throw new VersionBlockError({
133
+ problem: VERSION_BLOCK_PROBLEM.unterminated,
134
+ lineNumber: openBlock.lineNumber,
135
+ marker: openingMarker,
136
+ openingLineNumber: openBlock.lineNumber,
137
+ openingMarker,
138
+ });
139
+ }
140
+
141
+ return { content: lines.join(""), markedLines, replacements };
142
+ }
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Release version rules: only stable `X.Y.Z` versions exist, and after a
3
+ * version only its next patch, minor or major is allowed (from `1.2.3`:
4
+ * `1.2.4`, `1.3.0` or `2.0.0`). No version can be skipped, repeated or
5
+ * lowered, and prereleases or build metadata are never released.
6
+ *
7
+ * @module versions
8
+ */
9
+
10
+ import {
11
+ BREAKING_CHANGE_FOOTER_PATTERN,
12
+ CONVENTIONAL_HEADER_PATTERN,
13
+ FEATURE_COMMIT_TYPE,
14
+ LEGACY_FEATURE_SUBJECT_PATTERN,
15
+ PRE_MAJOR_SHIFTED_RELEASE_TYPE,
16
+ PRE_MAJOR_VERSION,
17
+ RELEASE_TAG_PREFIX,
18
+ RELEASE_TYPE,
19
+ RELEASE_TYPE_ORDER,
20
+ RELEASE_VERSION_PATTERN,
21
+ SEMVER_PATTERN,
22
+ UNRELEASED_PLACEHOLDER_VERSION,
23
+ } from "./constants/versions.js";
24
+
25
+ /**
26
+ * @typedef {"patch" | "minor" | "major"} ReleaseType
27
+ * @typedef {{ releaseType: ReleaseType, version: string }} NextVersion
28
+ */
29
+
30
+ /**
31
+ * Returns whether a value is a stable `X.Y.Z` release version.
32
+ *
33
+ * @param {unknown} version - Candidate version.
34
+ * @returns {boolean} `true` only for plain `X.Y.Z` without prerelease, metadata, prefix or leading zeros.
35
+ */
36
+ export function isStableReleaseVersion(version) {
37
+ return typeof version === "string" && RELEASE_VERSION_PATTERN.test(version);
38
+ }
39
+
40
+ /**
41
+ * Parses a stable `X.Y.Z` version.
42
+ *
43
+ * @param {string} version - Version such as `0.93.0`.
44
+ * @returns {[number, number, number]} Major, minor and patch numbers.
45
+ * @throws {Error} When the version is not a stable release version.
46
+ */
47
+ export function parseReleaseVersion(version) {
48
+ const match = RELEASE_VERSION_PATTERN.exec(String(version));
49
+
50
+ if (!match) {
51
+ throw new Error(`astra-rp:parseReleaseVersion expected X.Y.Z, received "${version}"`);
52
+ }
53
+
54
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
55
+ }
56
+
57
+ /**
58
+ * Compares two stable `X.Y.Z` versions numerically.
59
+ *
60
+ * @param {string} leftVersion - First version.
61
+ * @param {string} rightVersion - Second version.
62
+ * @returns {number} Negative when `leftVersion` is lower, `0` when equal, positive when higher.
63
+ * @throws {Error} When either version is not a stable release version.
64
+ */
65
+ export function compareReleaseVersions(leftVersion, rightVersion) {
66
+ const left = parseReleaseVersion(leftVersion);
67
+ const right = parseReleaseVersion(rightVersion);
68
+ const differentIndex = left.findIndex((part, index) => part !== right[index]);
69
+ return differentIndex === -1 ? 0 : left[differentIndex] - right[differentIndex];
70
+ }
71
+
72
+ /**
73
+ * Tells whether a stable version is above any semver version, prereleases included: `1.0.0` is
74
+ * above `1.0.0-beta.1` and `0.9.0`, but not above `1.1.0-beta.1`.
75
+ *
76
+ * @param {string} stableVersion - Stable `X.Y.Z` version.
77
+ * @param {string} otherVersion - Version to compare with, such as the `latest` dist-tag of npm.
78
+ * @returns {boolean} `true` when `stableVersion` is higher; `false` otherwise or when `otherVersion` is not semver.
79
+ */
80
+ export function isStableVersionAbove(stableVersion, otherVersion) {
81
+ const match = SEMVER_PATTERN.exec(String(otherVersion));
82
+
83
+ if (!match) {
84
+ return false;
85
+ }
86
+
87
+ const [, core, prerelease] = match;
88
+ const difference = compareReleaseVersions(stableVersion, core);
89
+ return difference > 0 || (difference === 0 && Boolean(prerelease));
90
+ }
91
+
92
+ /**
93
+ * Returns the highest stable version of a list, ignoring prereleases and invalid entries.
94
+ *
95
+ * @param {unknown[]} versions - Versions, such as the ones `npm view <pkg> versions` lists.
96
+ * @returns {string | null} Highest stable `X.Y.Z` version, or `null` when there is none.
97
+ */
98
+ export function findHighestStableVersion(versions) {
99
+ /** @type {string | null} */
100
+ let highest = null;
101
+
102
+ for (const version of versions) {
103
+ if (typeof version === "string" && isStableReleaseVersion(version) && (highest === null || compareReleaseVersions(version, highest) > 0)) {
104
+ highest = version;
105
+ }
106
+ }
107
+
108
+ return highest;
109
+ }
110
+
111
+ /**
112
+ * Returns the next version for a semver release type.
113
+ *
114
+ * @param {string} version - Current `X.Y.Z` version.
115
+ * @param {ReleaseType} releaseType - One of {@link RELEASE_TYPE}.
116
+ * @returns {string} Next `X.Y.Z` version.
117
+ * @throws {Error} When the version is not stable or the release type is unknown.
118
+ */
119
+ export function bumpReleaseVersion(version, releaseType) {
120
+ const [major, minor, patch] = parseReleaseVersion(version);
121
+
122
+ switch (releaseType) {
123
+ case RELEASE_TYPE.major:
124
+ return `${major + 1}.0.0`;
125
+ case RELEASE_TYPE.minor:
126
+ return `${major}.${minor + 1}.0`;
127
+ case RELEASE_TYPE.patch:
128
+ return `${major}.${minor}.${patch + 1}`;
129
+ default:
130
+ throw new Error(`astra-rp:bumpReleaseVersion unknown release type "${releaseType}"`);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Lists the only versions allowed after the current one: the next patch,
136
+ * minor and major. Anything else would skip versions or go backwards.
137
+ *
138
+ * @param {string} currentVersion - Current `X.Y.Z` version.
139
+ * @returns {NextVersion[]} Allowed next versions, patch first.
140
+ */
141
+ export function listNextVersions(currentVersion) {
142
+ return RELEASE_TYPE_ORDER.map((releaseType) => ({
143
+ releaseType,
144
+ version: bumpReleaseVersion(currentVersion, releaseType),
145
+ }));
146
+ }
147
+
148
+ /**
149
+ * Lists the versions allowed after any previous semver version. A previous
150
+ * prerelease is also followed by its own stable release (`1.0.0-beta.1` →
151
+ * `1.0.0`).
152
+ *
153
+ * @param {string} previousVersion - Previous version, stable or prerelease.
154
+ * @returns {string[] | null} Allowed versions, or `null` when the previous version is not semver.
155
+ */
156
+ export function listAllowedVersionsAfter(previousVersion) {
157
+ const match = SEMVER_PATTERN.exec(String(previousVersion));
158
+
159
+ if (!match) {
160
+ return null;
161
+ }
162
+
163
+ const [, core, prerelease] = match;
164
+ const nextVersions = listNextVersions(core).map((candidate) => candidate.version);
165
+
166
+ return prerelease ? [core, ...nextVersions] : nextVersions;
167
+ }
168
+
169
+ /**
170
+ * Resolves the version requested through `--bump` or `--set-version`.
171
+ *
172
+ * @param {string} currentVersion - Current `X.Y.Z` version.
173
+ * @param {{ bump: ReleaseType | null, setVersion: string | null }} request - Parsed CLI options.
174
+ * @returns {NextVersion | null} Requested version, or `null` to ask interactively.
175
+ * @throws {Error} With a Spanish message when the request is invalid.
176
+ */
177
+ export function resolveRequestedVersion(currentVersion, { bump, setVersion }) {
178
+ const nextVersions = listNextVersions(currentVersion);
179
+
180
+ if (bump) {
181
+ return nextVersions.find((candidate) => candidate.releaseType === bump) ?? null;
182
+ }
183
+
184
+ if (setVersion === null || setVersion === undefined) {
185
+ return null;
186
+ }
187
+
188
+ if (!isStableReleaseVersion(setVersion)) {
189
+ throw new Error(`--set-version espera el formato X.Y.Z y recibió "${setVersion}".`);
190
+ }
191
+
192
+ const match = nextVersions.find((candidate) => candidate.version === setVersion);
193
+
194
+ if (!match) {
195
+ const allowed = nextVersions.map((candidate) => candidate.version).join(", ");
196
+ throw new Error(
197
+ `--set-version ${setVersion} no es válida después de ${currentVersion}: tiene que ser mayor y no saltear versiones. Opciones: ${allowed}.`
198
+ );
199
+ }
200
+
201
+ return match;
202
+ }
203
+
204
+ /**
205
+ * Builds the Git tag name of a release version.
206
+ *
207
+ * @param {string} version - `X.Y.Z` version.
208
+ * @returns {string} Tag such as `v0.94.0`.
209
+ */
210
+ export function toReleaseTag(version) {
211
+ return `${RELEASE_TAG_PREFIX}${version}`;
212
+ }
213
+
214
+ /**
215
+ * Returns whether a commit subject is a release bump commit (`0.93.0`).
216
+ *
217
+ * @param {string} subject - Commit subject.
218
+ * @returns {boolean} `true` for version-only subjects.
219
+ */
220
+ export function isReleaseCommitSubject(subject) {
221
+ return isStableReleaseVersion(subject.trim());
222
+ }
223
+
224
+ /**
225
+ * Suggests the semver release type for the commits that will ship.
226
+ *
227
+ * Breaking changes suggest `major`; features (conventional `feat` or legacy
228
+ * imperative subjects such as "Add ...") suggest `minor`; a set made only of
229
+ * conventional maintenance commits (`fix`, `chore`, `docs`, ...) suggests
230
+ * `patch`. Unknown legacy subjects suggest `minor`.
231
+ *
232
+ * @param {{ subject: string, body?: string }[]} commits - Commits since the last release.
233
+ * @returns {{ releaseType: ReleaseType, reason: string }} Suggested type and a Spanish explanation.
234
+ */
235
+ export function suggestReleaseType(commits) {
236
+ const shippedCommits = commits.filter((commit) => !isReleaseCommitSubject(commit.subject));
237
+ let hasFeature = false;
238
+ let hasUnknownSubject = false;
239
+
240
+ for (const commit of shippedCommits) {
241
+ const header = CONVENTIONAL_HEADER_PATTERN.exec(commit.subject);
242
+
243
+ if (header?.groups?.breaking || BREAKING_CHANGE_FOOTER_PATTERN.test(commit.body ?? "")) {
244
+ return { releaseType: RELEASE_TYPE.major, reason: "hay cambios incompatibles (breaking change)" };
245
+ }
246
+
247
+ if (header?.groups) {
248
+ hasFeature ||= header.groups.type.toLowerCase() === FEATURE_COMMIT_TYPE;
249
+ continue;
250
+ }
251
+
252
+ if (LEGACY_FEATURE_SUBJECT_PATTERN.test(commit.subject)) {
253
+ hasFeature = true;
254
+ } else {
255
+ hasUnknownSubject = true;
256
+ }
257
+ }
258
+
259
+ if (hasFeature) {
260
+ return { releaseType: RELEASE_TYPE.minor, reason: "hay funcionalidades nuevas" };
261
+ }
262
+
263
+ if (hasUnknownSubject || shippedCommits.length === 0) {
264
+ return { releaseType: RELEASE_TYPE.minor, reason: "criterio habitual del repositorio" };
265
+ }
266
+
267
+ return { releaseType: RELEASE_TYPE.patch, reason: "solo hay arreglos y mantenimiento" };
268
+ }
269
+
270
+ /**
271
+ * Tells whether `preMajorShift` lowers the release levels after `currentVersion`: only when the
272
+ * option is on and the version is `0.x`, except the `0.0.0` placeholder of a package never released.
273
+ *
274
+ * @param {string} currentVersion - Current `X.Y.Z` version.
275
+ * @param {{ preMajorShift?: boolean }} [options] - Project versioning options.
276
+ * @returns {boolean} `true` when the release levels move one level down.
277
+ */
278
+ export function isPreMajorShiftActive(currentVersion, { preMajorShift = false } = {}) {
279
+ return preMajorShift && currentVersion !== UNRELEASED_PLACEHOLDER_VERSION && parseReleaseVersion(currentVersion)[0] === PRE_MAJOR_VERSION;
280
+ }
281
+
282
+ /**
283
+ * Suggests the release type of the next version after `currentVersion`: {@link suggestReleaseType},
284
+ * shifted one level down while the version is `0.x` when `preMajorShift` is on (breaking changes
285
+ * suggest `minor` and features `patch`, so a suggestion never jumps to `1.0.0`). The first release
286
+ * of a `0.0.0` package is not shifted.
287
+ *
288
+ * @param {{ subject: string, body?: string }[]} commits - Commits since the last release.
289
+ * @param {string} currentVersion - Current `X.Y.Z` version.
290
+ * @param {{ preMajorShift?: boolean }} [options] - Project versioning options.
291
+ * @returns {{ releaseType: ReleaseType, reason: string }} Suggested type and a Spanish explanation.
292
+ */
293
+ export function suggestNextReleaseType(commits, currentVersion, { preMajorShift = false } = {}) {
294
+ const suggestion = suggestReleaseType(commits);
295
+ if (!isPreMajorShiftActive(currentVersion, { preMajorShift })) {
296
+ return suggestion;
297
+ }
298
+
299
+ const shiftedReleaseType = PRE_MAJOR_SHIFTED_RELEASE_TYPE[suggestion.releaseType];
300
+ return shiftedReleaseType === suggestion.releaseType
301
+ ? suggestion
302
+ : { releaseType: shiftedReleaseType, reason: `${suggestion.reason}; antes de 1.0.0 baja un nivel` };
303
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Decides whether Vercel builds a commit (`vercel.json` → `ignoreCommand`).
3
+ *
4
+ * Only a stable `X.Y.Z` version that is the next patch, minor or major of the
5
+ * previous commit's version ships, the same rule `create-version` applies. An
6
+ * unchanged, lower or skipped version (`1.0.0` → `3.0.0`) and any prerelease
7
+ * or build-metadata version skip the build. When the previous version cannot
8
+ * be read there is nothing to compare, so a stable version builds.
9
+ *
10
+ * A release delegated to CI is skipped only on the commit that changes
11
+ * `.astra-rp/release.json` against its first parent, the same commit scoping the
12
+ * generated `.astra-rp/vercel-ignore-build.mjs` applies (that template stays
13
+ * dependency-free, so it inlines the check). Later commits keep the metadata
14
+ * untouched and fall through to the version rule. Without a readable parent
15
+ * (shallow clone or first commit) the release commit is recognized by its own
16
+ * `Astra-Rp-Execution: ci` trailer, which later commits never carry, so the
17
+ * deployment still waits for the release workflow.
18
+ *
19
+ * @module build-gate
20
+ */
21
+ export type BuildDecision = {
22
+ shouldBuild: boolean;
23
+ reason: string;
24
+ };
25
+ /**
26
+ * @typedef {{ shouldBuild: boolean, reason: string }} BuildDecision
27
+ */
28
+ /**
29
+ * Decides whether a commit is built.
30
+ *
31
+ * @param {string | null} previousVersion - Version of the previous commit, or `null` when unreadable.
32
+ * @param {string | null} currentVersion - Version of the commit being deployed, or `null` when unreadable.
33
+ * @returns {BuildDecision} Decision and a log line.
34
+ */
35
+ export declare function decideBuild(previousVersion: string | null, currentVersion: string | null): BuildDecision;
36
+ /**
37
+ * Reads the previous and the current `package.json` versions of a Git
38
+ * checkout and decides whether it is built.
39
+ *
40
+ * @param {string} repositoryRoot - Checkout whose `HEAD` is being deployed.
41
+ * @returns {BuildDecision} Decision and a log line.
42
+ */
43
+ export declare function decideBuildForCheckout(repositoryRoot: string): BuildDecision;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Internal helpers that split `CHANGELOG.md` into release blocks and rewrite the `[Unreleased]`
3
+ * block. Shared by the public `@astra-kit/rp/changelog` module and by `create-version`; not part of the
4
+ * package's public API (no `exports` entry and no reexport from `@astra-kit/rp`).
5
+ *
6
+ * @module changelog-blocks
7
+ */
8
+ export type ChangelogBlock = {
9
+ label: string;
10
+ heading: string;
11
+ start: number;
12
+ bodyStart: number;
13
+ end: number;
14
+ };
15
+ /**
16
+ * @typedef {{ label: string, heading: string, start: number, bodyStart: number, end: number }} ChangelogBlock
17
+ */
18
+ /**
19
+ * Splits the changelog into release blocks in document order.
20
+ *
21
+ * @param {string} changelog - CHANGELOG.md contents.
22
+ * @returns {ChangelogBlock[]} Blocks.
23
+ */
24
+ export declare function listBlocks(changelog: string): ChangelogBlock[];
25
+ /**
26
+ * Replaces the `[Unreleased]` block with `body`, or inserts the block above the newest release.
27
+ *
28
+ * @param {string} changelog - Existing contents, or an empty string for a new file.
29
+ * @param {string} body - New block body, already using `lineBreak`.
30
+ * @param {string} lineBreak - Line break written around the body.
31
+ * @returns {string} Changelog with the new `[Unreleased]` block.
32
+ */
33
+ export declare function replaceUnreleasedBody(changelog: string, body: string, lineBreak: string): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Generates CHANGELOG.md entries from commits and releases `[Unreleased]`.
3
+ *
4
+ * A release lists the hash and original subject of every commit since the
5
+ * previous version, then moves the block under `## [X.Y.Z] - YYYY-MM-DD`.
6
+ * Published history, including legacy unbracketed headings, is preserved.
7
+ *
8
+ * `astra-rp create-version` does not generate entries from commits: it requires entries under
9
+ * `[Unreleased]`, written by hand or, with `changelogDraft`, drafted by an agent CLI, and in the
10
+ * release commit only renames that heading to `## [X.Y.Z] - YYYY-MM-DD` and adds an empty
11
+ * `[Unreleased]` on top, keeping any section names.
12
+ *
13
+ * @module changelog
14
+ */
15
+ /**
16
+ * Replaces `[Unreleased]` with the exact commit list for the next version.
17
+ * Creates the block or document when missing and preserves released history.
18
+ *
19
+ * @param {string} changelog - Existing contents, or an empty string for a new file.
20
+ * @param {{ sha: string, subject: string }[]} commits - Commits since the previous release, newest first.
21
+ * @returns {string} Changelog with one hash and original subject per commit.
22
+ * @throws {Error} When there are no commits to release.
23
+ */
24
+ export declare function fillUnreleasedFromCommits(changelog: string, commits: {
25
+ sha: string;
26
+ subject: string;
27
+ }[]): string;
28
+ export type ChangelogBlock = import("./changelog-blocks.js").ChangelogBlock;
29
+ export type UnreleasedState = {
30
+ exists: boolean;
31
+ entryCount: number;
32
+ unknownSections: string[];
33
+ body: string;
34
+ };
35
+ /**
36
+ * Describes the `## [Unreleased]` block.
37
+ *
38
+ * @param {string} changelog - CHANGELOG.md contents.
39
+ * @returns {UnreleasedState} Unreleased state.
40
+ */
41
+ export declare function readUnreleased(changelog: string): UnreleasedState;
42
+ /**
43
+ * Returns the newest released block (the first one that is not `[Unreleased]`).
44
+ *
45
+ * @param {string} changelog - CHANGELOG.md contents.
46
+ * @returns {{ version: string, entryCount: number } | null} Latest release entry.
47
+ */
48
+ export declare function readLatestRelease(changelog: string): {
49
+ version: string;
50
+ entryCount: number;
51
+ } | null;
52
+ /**
53
+ * Moves the `[Unreleased]` changes under a new version heading and leaves an empty `[Unreleased]` block.
54
+ *
55
+ * @param {string} changelog - CHANGELOG.md contents.
56
+ * @param {string} version - Version being released.
57
+ * @param {string} releaseDate - Date in `YYYY-MM-DD` format.
58
+ * @returns {string} Released changelog.
59
+ * @throws {Error} When `[Unreleased]` is missing, empty or uses an unknown section.
60
+ */
61
+ export declare function releaseUnreleased(changelog: string, version: string, releaseDate: string): string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Contract between the `astra-rp ignore-build` command and the Vercel
3
+ * `ignoreCommand` wrapper that reads its output.
4
+ *
5
+ * @module constants/build-gate
6
+ */
7
+ /** Decision printed as the last output line of `astra-rp ignore-build`. */
8
+ export declare const BUILD_DECISION: Readonly<{
9
+ build: "BUILD";
10
+ skip: "SKIP";
11
+ }>;
12
+ /** Git revision whose `package.json` holds the previous version. */
13
+ export declare const PREVIOUS_REVISION = "HEAD^";
14
+ /** Git revision being deployed. */
15
+ export declare const CURRENT_REVISION = "HEAD";
16
+ /** `git diff --quiet` status reporting that the compared path changed between both revisions. */
17
+ export declare const GIT_DIFF_CHANGED_STATUS = 1;
18
+ /** Manifest that holds the version. */
19
+ export declare const PACKAGE_MANIFEST_FILE = "package.json";
20
+ /** Exit code of `astra-rp ignore-build` when a decision was printed. */
21
+ export declare const DECISION_EXIT_CODE = 0;
22
+ /** Exit code of `astra-rp ignore-build` when it could not decide; wrappers must skip the build. */
23
+ export declare const GATE_FAILURE_EXIT_CODE = 2;