Skip to content

Commit 1a584f6

Browse files
committed
refactor: extract semver utilities to dedicated src/semver.mjs module
Signed-off-by: leocavalcante <[email protected]>
1 parent a59046b commit 1a584f6

File tree

5 files changed

+525
-165
lines changed

5 files changed

+525
-165
lines changed

src/paths.d.mts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
* Type declarations for paths.mjs
33
*/
44

5+
// Re-export semver types for backwards compatibility
6+
export {
7+
type ParsedVersion,
8+
checkVersionCompatibility,
9+
compareVersions,
10+
parseVersion,
11+
} from "./semver.mjs"
12+
513
/** Minimum character count for valid agent files */
614
export declare const MIN_CONTENT_LENGTH: number
715

@@ -158,31 +166,6 @@ export interface ValidateAgentContentResult {
158166
*/
159167
export function validateAgentContent(content: string): ValidateAgentContentResult
160168

161-
/**
162-
* Checks if a version satisfies a semver range requirement.
163-
*
164-
* Supports common semver range patterns:
165-
* - Exact version: "1.0.0" (must match exactly)
166-
* - Greater than or equal: ">=1.0.0"
167-
* - Greater than: ">1.0.0"
168-
* - Less than or equal: "<=1.0.0"
169-
* - Less than: "<1.0.0"
170-
* - Caret (compatible with): "^1.0.0" (>=1.0.0 and <2.0.0)
171-
* - Tilde (approximately): "~1.2.0" (>=1.2.0 and <1.3.0)
172-
*
173-
* @param required - The required version range (e.g., ">=0.1.0", "^1.0.0")
174-
* @param current - The current version to check (e.g., "1.2.3")
175-
* @returns True if current version satisfies the required range
176-
*
177-
* @example
178-
* checkVersionCompatibility(">=0.1.0", "0.2.0") // true
179-
* checkVersionCompatibility("^1.0.0", "1.5.0") // true
180-
* checkVersionCompatibility("^1.0.0", "2.0.0") // false
181-
* checkVersionCompatibility("~1.2.0", "1.2.5") // true
182-
* checkVersionCompatibility("~1.2.0", "1.3.0") // false
183-
*/
184-
export function checkVersionCompatibility(required: string, current: string): boolean
185-
186169
/**
187170
* Parsed command line flags for install/uninstall scripts.
188171
*/

src/paths.mjs

Lines changed: 7 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import { homedir } from "node:os"
99
import { dirname, join } from "node:path"
1010
import { fileURLToPath } from "node:url"
1111

12+
// Re-export semver utilities for backwards compatibility
13+
export {
14+
checkVersionCompatibility,
15+
compareVersions,
16+
parseVersion,
17+
} from "./semver.mjs"
18+
1219
/** Minimum character count for valid agent files */
1320
export const MIN_CONTENT_LENGTH = 100
1421

@@ -303,146 +310,6 @@ export function validateAgentContent(content) {
303310
return { valid: true }
304311
}
305312

306-
/**
307-
* Parses a semver version string into its numeric components.
308-
*
309-
* @param {string} version - The version string (e.g., "1.2.3")
310-
* @returns {{ major: number, minor: number, patch: number } | null} Parsed version or null if invalid
311-
*/
312-
function parseVersion(version) {
313-
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/)
314-
if (!match) return null
315-
return {
316-
major: Number.parseInt(match[1], 10),
317-
minor: Number.parseInt(match[2], 10),
318-
patch: Number.parseInt(match[3], 10),
319-
}
320-
}
321-
322-
/**
323-
* Compares two parsed version objects.
324-
*
325-
* @param {{ major: number, minor: number, patch: number }} a - First version
326-
* @param {{ major: number, minor: number, patch: number }} b - Second version
327-
* @returns {number} -1 if a < b, 0 if a == b, 1 if a > b
328-
*/
329-
function compareVersions(a, b) {
330-
if (a.major !== b.major) return a.major < b.major ? -1 : 1
331-
if (a.minor !== b.minor) return a.minor < b.minor ? -1 : 1
332-
if (a.patch !== b.patch) return a.patch < b.patch ? -1 : 1
333-
return 0
334-
}
335-
336-
/**
337-
* Checks if a version satisfies a semver range requirement.
338-
*
339-
* Supports common semver range patterns:
340-
* - Exact version: "1.0.0" (must match exactly)
341-
* - Greater than or equal: ">=1.0.0"
342-
* - Greater than: ">1.0.0"
343-
* - Less than or equal: "<=1.0.0"
344-
* - Less than: "<1.0.0"
345-
* - Caret (compatible with): "^1.0.0" (>=1.0.0 and <2.0.0)
346-
* - Tilde (approximately): "~1.2.0" (>=1.2.0 and <1.3.0)
347-
*
348-
* @param {string} required - The required version range (e.g., ">=0.1.0", "^1.0.0")
349-
* @param {string} current - The current version to check (e.g., "1.2.3")
350-
* @returns {boolean} True if current version satisfies the required range
351-
*
352-
* @example
353-
* checkVersionCompatibility(">=0.1.0", "0.2.0") // true
354-
* checkVersionCompatibility("^1.0.0", "1.5.0") // true
355-
* checkVersionCompatibility("^1.0.0", "2.0.0") // false
356-
* checkVersionCompatibility("~1.2.0", "1.2.5") // true
357-
* checkVersionCompatibility("~1.2.0", "1.3.0") // false
358-
* @throws {TypeError} If required or current is not a non-empty string
359-
*/
360-
export function checkVersionCompatibility(required, current) {
361-
if (typeof required !== "string") {
362-
throw new TypeError(
363-
`checkVersionCompatibility: required must be a string, got ${required === null ? "null" : typeof required}`,
364-
)
365-
}
366-
if (required.trim() === "") {
367-
throw new TypeError("checkVersionCompatibility: required must not be empty")
368-
}
369-
if (typeof current !== "string") {
370-
throw new TypeError(
371-
`checkVersionCompatibility: current must be a string, got ${current === null ? "null" : typeof current}`,
372-
)
373-
}
374-
if (current.trim() === "") {
375-
throw new TypeError("checkVersionCompatibility: current must not be empty")
376-
}
377-
const currentVersion = parseVersion(current)
378-
if (!currentVersion) return false
379-
380-
// Handle caret range: ^1.0.0 means >=1.0.0 and <2.0.0 (for major >= 1)
381-
// For ^0.x.y, it means >=0.x.y and <0.(x+1).0
382-
if (required.startsWith("^")) {
383-
const rangeVersion = parseVersion(required.slice(1))
384-
if (!rangeVersion) return false
385-
386-
// Must be >= the specified version
387-
if (compareVersions(currentVersion, rangeVersion) < 0) return false
388-
389-
// For major version 0, only minor version must match
390-
if (rangeVersion.major === 0) {
391-
return currentVersion.major === 0 && currentVersion.minor === rangeVersion.minor
392-
}
393-
394-
// Major version must match
395-
return currentVersion.major === rangeVersion.major
396-
}
397-
398-
// Handle tilde range: ~1.2.0 means >=1.2.0 and <1.3.0
399-
if (required.startsWith("~")) {
400-
const rangeVersion = parseVersion(required.slice(1))
401-
if (!rangeVersion) return false
402-
403-
// Must be >= the specified version
404-
if (compareVersions(currentVersion, rangeVersion) < 0) return false
405-
406-
// Major and minor must match
407-
return (
408-
currentVersion.major === rangeVersion.major && currentVersion.minor === rangeVersion.minor
409-
)
410-
}
411-
412-
// Handle >= operator
413-
if (required.startsWith(">=")) {
414-
const rangeVersion = parseVersion(required.slice(2))
415-
if (!rangeVersion) return false
416-
return compareVersions(currentVersion, rangeVersion) >= 0
417-
}
418-
419-
// Handle > operator
420-
if (required.startsWith(">")) {
421-
const rangeVersion = parseVersion(required.slice(1))
422-
if (!rangeVersion) return false
423-
return compareVersions(currentVersion, rangeVersion) > 0
424-
}
425-
426-
// Handle <= operator
427-
if (required.startsWith("<=")) {
428-
const rangeVersion = parseVersion(required.slice(2))
429-
if (!rangeVersion) return false
430-
return compareVersions(currentVersion, rangeVersion) <= 0
431-
}
432-
433-
// Handle < operator
434-
if (required.startsWith("<")) {
435-
const rangeVersion = parseVersion(required.slice(1))
436-
if (!rangeVersion) return false
437-
return compareVersions(currentVersion, rangeVersion) < 0
438-
}
439-
440-
// Handle exact version match
441-
const rangeVersion = parseVersion(required)
442-
if (!rangeVersion) return false
443-
return compareVersions(currentVersion, rangeVersion) === 0
444-
}
445-
446313
/**
447314
* Parses command line flags for install/uninstall scripts.
448315
*

src/semver.d.mts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Type declarations for semver.mjs
3+
*/
4+
5+
/**
6+
* Parsed semver version object.
7+
*/
8+
export interface ParsedVersion {
9+
major: number
10+
minor: number
11+
patch: number
12+
}
13+
14+
/**
15+
* Parses a semver version string into its numeric components.
16+
*
17+
* @param version - The version string (e.g., "1.2.3")
18+
* @returns Parsed version object or null if invalid
19+
*
20+
* @example
21+
* parseVersion("1.2.3") // { major: 1, minor: 2, patch: 3 }
22+
* parseVersion("invalid") // null
23+
*/
24+
export function parseVersion(version: string): ParsedVersion | null
25+
26+
/**
27+
* Compares two parsed version objects.
28+
*
29+
* @param a - First version
30+
* @param b - Second version
31+
* @returns -1 if a < b, 0 if a == b, 1 if a > b
32+
*
33+
* @example
34+
* compareVersions({ major: 1, minor: 0, patch: 0 }, { major: 2, minor: 0, patch: 0 }) // -1
35+
* compareVersions({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 0 }) // 0
36+
* compareVersions({ major: 2, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 0 }) // 1
37+
*/
38+
export function compareVersions(a: ParsedVersion, b: ParsedVersion): -1 | 0 | 1
39+
40+
/**
41+
* Checks if a version satisfies a semver range requirement.
42+
*
43+
* Supports common semver range patterns:
44+
* - Exact version: "1.0.0" (must match exactly)
45+
* - Greater than or equal: ">=1.0.0"
46+
* - Greater than: ">1.0.0"
47+
* - Less than or equal: "<=1.0.0"
48+
* - Less than: "<1.0.0"
49+
* - Caret (compatible with): "^1.0.0" (>=1.0.0 and <2.0.0)
50+
* - Tilde (approximately): "~1.2.0" (>=1.2.0 and <1.3.0)
51+
*
52+
* @param required - The required version range (e.g., ">=0.1.0", "^1.0.0")
53+
* @param current - The current version to check (e.g., "1.2.3")
54+
* @returns True if current version satisfies the required range
55+
*
56+
* @example
57+
* checkVersionCompatibility(">=0.1.0", "0.2.0") // true
58+
* checkVersionCompatibility("^1.0.0", "1.5.0") // true
59+
* checkVersionCompatibility("^1.0.0", "2.0.0") // false
60+
* checkVersionCompatibility("~1.2.0", "1.2.5") // true
61+
* checkVersionCompatibility("~1.2.0", "1.3.0") // false
62+
*/
63+
export function checkVersionCompatibility(required: string, current: string): boolean

0 commit comments

Comments
 (0)