// A deliberately tiny semver range check — enough for `coreApi` and no more. // // Supports `*`, an exact `x.y.z`, `^x.y.z` and `~x.y.z`. That is the whole // grammar a module manifest is allowed to use (MODULE_API.md §1.1), so pulling // in the `semver` package for it would add a dependency to the server for a // twenty-line job. A range this parser does not understand is REJECTED rather // than assumed to match — an unparseable range must not silently load a module // against an API it was never tested on. const PARTS = /^(\d+)\.(\d+)\.(\d+)$/ function parse(version) { const m = PARTS.exec(String(version).trim()) if (!m) return null return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) } } const gte = (a, b) => { if (a.major !== b.major) return a.major > b.major if (a.minor !== b.minor) return a.minor > b.minor return a.patch >= b.patch } /** * Does `version` satisfy `range`? * @param {string} version an exact x.y.z * @param {string} range `*` | `x.y.z` | `^x.y.z` | `~x.y.z` * @returns {boolean} false for anything unparseable, on either side */ function satisfies(version, range) { const v = parse(version) if (!v) return false const raw = String(range).trim() if (raw === '*') return true const op = raw[0] === '^' || raw[0] === '~' ? raw[0] : '' const b = parse(op ? raw.slice(1) : raw) if (!b) return false if (op === '') return v.major === b.major && v.minor === b.minor && v.patch === b.patch if (!gte(v, b)) return false // ^ allows minor+patch within the same major; ~ allows patch within the same minor. if (op === '^') return v.major === b.major return v.major === b.major && v.minor === b.minor } module.exports = { satisfies, parse }