@vltpkg/semver
Advanced tools
| import { Version } from './version.ts'; | ||
| /** all comparators are expressed in terms of these operators */ | ||
| export type SimpleOperator = '' | '<' | '<=' | '>' | '>='; | ||
| /** operators that are expanded to simpler forms */ | ||
| export type ComplexOperator = '^' | '~' | '~>'; | ||
| /** comparator expressed as a [operator,version] tuple */ | ||
| export type OVTuple = [SimpleOperator, Version]; | ||
| /** | ||
| * The result of parsing a version value that might be either a full | ||
| * version like `1.2.3` or an X-Range like `1.2.x` | ||
| */ | ||
| export type ParsedXRange = ParsedXMajor | ParsedXMinor | ParsedXPatch | ParsedXVersion; | ||
| /** | ||
| * a {@link ParsedXRange} that is just a `*` | ||
| */ | ||
| export type ParsedXMajor = []; | ||
| /** | ||
| * a {@link ParsedXRange} that is just a major version | ||
| */ | ||
| export type ParsedXMinor = [number]; | ||
| /** | ||
| * a {@link ParsedXRange} that is just a major and minor version | ||
| */ | ||
| export type ParsedXPatch = [number, number]; | ||
| /** | ||
| * a {@link ParsedXRange} that is a full version | ||
| */ | ||
| export type ParsedXVersion = [ | ||
| M: number, | ||
| m: number, | ||
| p: number, | ||
| pr?: string | undefined, | ||
| b?: string | undefined | ||
| ]; | ||
| /** | ||
| * Class used to parse the `||` separated portions | ||
| * of a range, and evaluate versions against it. | ||
| * | ||
| * This does most of the heavy lifting of range testing, and provides | ||
| * little affordance for improperly formatted strings. It should be | ||
| * considered an internal class, and usually not accessed directly. | ||
| */ | ||
| export declare class Comparator { | ||
| #private; | ||
| /** | ||
| * does this range include prereleases, even when they do not | ||
| * match the tuple in the comparator? | ||
| */ | ||
| includePrerelease: boolean; | ||
| /** raw string used to create this comparator */ | ||
| raw: string; | ||
| /** tokens extracted from the raw string input */ | ||
| tokens: string[]; | ||
| /** | ||
| * Either the `any` comparator, the `none` comparator, or an operator | ||
| * and a {@link ParsedXRange} | ||
| */ | ||
| tuples: (Comparator | OVTuple)[]; | ||
| /** true if this comparator can not match anything */ | ||
| isNone: boolean; | ||
| /** | ||
| * true if this comparator is a `'*'` type of range. | ||
| * | ||
| * Note that it still will not match versions with a prerelease value, | ||
| * unless the tuple in the version matches the tuple provided to the | ||
| * comparator, and the comparator version also has a prerelease value, | ||
| * unless `includePrerelease` is set. | ||
| */ | ||
| isAny: boolean; | ||
| /** the canonical strict simplified parsed form of this constructor */ | ||
| toString(): string; | ||
| constructor(comp: string, includePrerelease?: boolean); | ||
| /** return true if the version is a match for this comparator */ | ||
| test(v: Version): boolean; | ||
| } |
| // TODO: it might be faster to not have Version objects in the | ||
| // comparator tuples, and instead just keep the parsed number arrays? | ||
| import { syntaxError } from '@vltpkg/error-cause'; | ||
| import { fastSplit } from '@vltpkg/fast-split'; | ||
| import { Version } from "./version.js"; | ||
| const isOperator = (o) => !!o && | ||
| (o === '>' || | ||
| o === '<' || | ||
| o === '>=' || | ||
| o === '<=' || | ||
| o === '' || | ||
| o === '~' || | ||
| o === '^' || | ||
| o === '~>'); | ||
| const preJunk = new Set('=v \t'); | ||
| const invalidComp = (c, message) => syntaxError(`invalid comparator: '${c}' ${message}`, { found: c }, Comparator); | ||
| const assertNumber = (value, c, field) => { | ||
| const n = Number(value); | ||
| if (n !== n) { | ||
| throw invalidComp(c, `${field} must be numeric or 'x', got: '${value}'`); | ||
| } | ||
| return n; | ||
| }; | ||
| const assertVersion = (v, comp) => { | ||
| if (!v) { | ||
| throw invalidComp(comp, 'no value provided for operator'); | ||
| } | ||
| }; | ||
| const assertMissing = (value, c, field) => { | ||
| if (value && !isX(value)) { | ||
| throw invalidComp(c, `cannot omit '${field}' and include subsequent fields`); | ||
| } | ||
| }; | ||
| const MAJOR = 0; | ||
| const MINOR = 1; | ||
| const PATCH = 2; | ||
| const isX = (c) => !c || c === 'X' || c === 'x' || c === '*'; | ||
| const isFullVersion = (parsed) => undefined !== parsed[PATCH]; | ||
| const isXPatch = (parsed) => undefined !== parsed[MINOR] && undefined === parsed[PATCH]; | ||
| const isXMinor = (parsed) => undefined !== parsed[MAJOR] && undefined === parsed[MINOR]; | ||
| const isXMajor = (parsed) => undefined === parsed[MAJOR]; | ||
| /** | ||
| * Class used to parse the `||` separated portions | ||
| * of a range, and evaluate versions against it. | ||
| * | ||
| * This does most of the heavy lifting of range testing, and provides | ||
| * little affordance for improperly formatted strings. It should be | ||
| * considered an internal class, and usually not accessed directly. | ||
| */ | ||
| export class Comparator { | ||
| /** | ||
| * does this range include prereleases, even when they do not | ||
| * match the tuple in the comparator? | ||
| */ | ||
| includePrerelease; | ||
| /** raw string used to create this comparator */ | ||
| raw; | ||
| /** tokens extracted from the raw string input */ | ||
| tokens; | ||
| /** | ||
| * Either the `any` comparator, the `none` comparator, or an operator | ||
| * and a {@link ParsedXRange} | ||
| */ | ||
| tuples = []; | ||
| /** true if this comparator can not match anything */ | ||
| isNone = false; | ||
| /** | ||
| * true if this comparator is a `'*'` type of range. | ||
| * | ||
| * Note that it still will not match versions with a prerelease value, | ||
| * unless the tuple in the version matches the tuple provided to the | ||
| * comparator, and the comparator version also has a prerelease value, | ||
| * unless `includePrerelease` is set. | ||
| */ | ||
| isAny = false; | ||
| /** the canonical strict simplified parsed form of this constructor */ | ||
| toString() { | ||
| return (this.isNone ? '<0.0.0-0' | ||
| : this.isAny ? '*' | ||
| : /* c8 ignore next */ | ||
| this.tuples.map(c => (isAny(c) ? '*' : c.join(''))).join(' ')); | ||
| } | ||
| constructor(comp, includePrerelease = false) { | ||
| this.includePrerelease = includePrerelease; | ||
| comp = comp.trim(); | ||
| this.raw = comp; | ||
| let hyphen = false; | ||
| const rawComps = fastSplit(comp, ' ', -1, (part, parts, i) => { | ||
| if (part === '-') { | ||
| if (hyphen) { | ||
| throw invalidComp(comp, 'multiple hyphen ranges not allowed'); | ||
| } | ||
| if (parts.length !== 1 || i === -1) { | ||
| throw invalidComp(comp, 'hyphen must be between two versions'); | ||
| } | ||
| hyphen = true; | ||
| } | ||
| else if (hyphen && parts.length !== 2) { | ||
| throw invalidComp(comp, 'hyphen range must be alone'); | ||
| } | ||
| }); | ||
| // remove excess spaces, `> 1 2` => `>1 2` | ||
| const comps = []; | ||
| let followingOperator = false; | ||
| let l = 0; | ||
| for (const c of rawComps) { | ||
| if (c === '') | ||
| continue; | ||
| if (!followingOperator) { | ||
| followingOperator = isOperator(c); | ||
| comps.push(c); | ||
| l++; | ||
| continue; | ||
| } | ||
| // we know this is not undefined since followingOperator guards that | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| comps[l - 1] += c; | ||
| followingOperator = false; | ||
| } | ||
| // TS mistakenly thinks hyphen is always false here | ||
| if (hyphen) { | ||
| const [min, _, max] = comps; | ||
| /* c8 ignore start - defense in depth for TS, already guaranteed */ | ||
| if (!min || !max) { | ||
| throw invalidComp(comp, 'hyphen must be between two versions'); | ||
| } | ||
| /* c8 ignore stop */ | ||
| this.#parseHyphenRange(min, max); | ||
| } | ||
| else if (!comps.length || | ||
| (comps.length === 1 && isX(comps[0]))) { | ||
| this.tuples.push(this.#getComparatorAny()); | ||
| } | ||
| else { | ||
| for (const c of comps) { | ||
| this.#parse(c); | ||
| if (this.isNone) | ||
| break; | ||
| } | ||
| } | ||
| this.tokens = comps; | ||
| this.isAny = true; | ||
| for (const c of this.tuples) { | ||
| if (Array.isArray(c) || !c.isAny) { | ||
| this.isAny = false; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| // inclusive min | ||
| #xInclusiveMin(raw) { | ||
| const z = this.includePrerelease ? '0' : undefined; | ||
| const [M, m = 0, p = 0, pr = z, build] = this.#parseX(raw); | ||
| return M === undefined ? | ||
| this.#getComparatorAny() | ||
| : ['>=', new Version(raw, M, m, p, pr, build)]; | ||
| } | ||
| // exclusive min | ||
| #xExclusiveMin(raw) { | ||
| const parsed = this.#parseX(raw); | ||
| if (isFullVersion(parsed)) { | ||
| return ['>', new Version(raw, ...parsed)]; | ||
| } | ||
| const z = this.includePrerelease ? '0' : undefined; | ||
| if (isXPatch(parsed)) { | ||
| // >1.2 => >=1.3.0 | ||
| return [ | ||
| '>=', | ||
| new Version(raw, parsed[MAJOR], parsed[MINOR] + 1, 0, z, undefined), | ||
| ]; | ||
| } | ||
| if (isXMinor(parsed)) { | ||
| // >1 => >=2.0.0 | ||
| return [ | ||
| '>=', | ||
| new Version(raw, parsed[MAJOR] + 1, 0, 0, z, undefined), | ||
| ]; | ||
| } | ||
| this.isNone = true; | ||
| this.tuples.length = 0; | ||
| return comparatorNone; | ||
| } | ||
| #xInclusiveMax(raw) { | ||
| const parsed = this.#parseX(raw); | ||
| return (isFullVersion(parsed) ? ['<=', new Version(raw, ...parsed)] | ||
| : isXPatch(parsed) ? | ||
| [ | ||
| '<', | ||
| new Version(raw, parsed[MAJOR], parsed[MINOR] + 1, 0, '0', undefined), | ||
| ] | ||
| : isXMinor(parsed) ? | ||
| [ | ||
| '<', | ||
| new Version(raw, parsed[MAJOR] + 1, 0, 0, '0', undefined), | ||
| ] | ||
| : this.#getComparatorAny()); | ||
| } | ||
| #xExclusiveMax(raw) { | ||
| const z = this.includePrerelease ? '0' : undefined; | ||
| const [M = 0, m = 0, p = 0, pr = z, build] = this.#parseX(raw); | ||
| if (M === 0 && m === 0 && p === 0 && pr === '0') { | ||
| this.isNone = true; | ||
| this.tuples.length = 0; | ||
| return comparatorNone; | ||
| } | ||
| return ['<', new Version(raw, M, m, p, pr, build)]; | ||
| } | ||
| #validXM(raw, m, p) { | ||
| assertMissing(m, raw, 'major'); | ||
| assertMissing(p, raw, 'major'); | ||
| if (m === '' || p === '') { | ||
| throw invalidComp(raw, `(Did you mean '*'?)`); | ||
| } | ||
| return []; | ||
| } | ||
| #validXm(raw, M, m, p) { | ||
| assertMissing(p, raw, 'major'); | ||
| if (m === '' || p === '') { | ||
| throw invalidComp(raw, `(Did you mean '${M}'?)`); | ||
| } | ||
| return [assertNumber(M, raw, 'major')]; | ||
| } | ||
| #validXp(raw, M, m, p) { | ||
| if (p === '') { | ||
| throw invalidComp(raw, `(Did you mean '${M}.${m}'?)`); | ||
| } | ||
| return [ | ||
| assertNumber(M, raw, 'major'), | ||
| assertNumber(m, raw, 'minor'), | ||
| ]; | ||
| } | ||
| #validTuple(raw, M, m, p) { | ||
| return [ | ||
| assertNumber(M, raw, 'major'), | ||
| assertNumber(m, raw, 'minor'), | ||
| assertNumber(p, raw, 'patch'), | ||
| ]; | ||
| } | ||
| #validXbuild(raw, M, m, p, pl) { | ||
| // build, no prerelease | ||
| const patch = p.substring(0, pl); | ||
| const build = p.substring(pl + 1); | ||
| if (!patch) { | ||
| throw invalidComp(raw, 'cannot specify build without patch'); | ||
| } | ||
| if (!build) { | ||
| throw invalidComp(raw, `encountered '+', but no build value`); | ||
| } | ||
| return [ | ||
| assertNumber(M, raw, 'major'), | ||
| assertNumber(m, raw, 'minor'), | ||
| assertNumber(patch, raw, 'patch'), | ||
| undefined, | ||
| build, | ||
| ]; | ||
| } | ||
| #validXpr(raw, M, m, p, hy) { | ||
| { | ||
| // prerelease, no build | ||
| const patch = p.substring(0, hy); | ||
| const pr = p.substring(hy + 1); | ||
| if (!patch) { | ||
| throw invalidComp(raw, 'cannot specify prerelease without patch'); | ||
| } | ||
| if (!pr) { | ||
| throw invalidComp(raw, `encountered '-', but no prerelease value`); | ||
| } | ||
| return [ | ||
| assertNumber(M, raw, 'major'), | ||
| assertNumber(m, raw, 'minor'), | ||
| assertNumber(patch, raw, 'patch'), | ||
| pr, | ||
| undefined, | ||
| ]; | ||
| } | ||
| } | ||
| #validXprbuild(raw, M, m, p, hy, pl) { | ||
| // both prerelease and build | ||
| const patch = p.substring(0, hy); | ||
| const pr = p.substring(hy + 1, pl); | ||
| const build = p.substring(pl + 1); | ||
| if (!patch) { | ||
| throw invalidComp(raw, 'cannot specify prerelease without patch'); | ||
| } | ||
| if (!pr) { | ||
| throw invalidComp(raw, `encountered '-', but no prerelease value`); | ||
| } | ||
| if (!build) { | ||
| throw invalidComp(raw, `encountered '+', but no build value`); | ||
| } | ||
| return [ | ||
| assertNumber(M, raw, 'major'), | ||
| assertNumber(m, raw, 'minor'), | ||
| assertNumber(patch, raw, 'patch'), | ||
| pr, | ||
| build, | ||
| ]; | ||
| } | ||
| // pull the relevant values out of an X-range or version | ||
| // return the fields for creating a Version object. | ||
| // only call once operator is stripped off | ||
| #parseX(raw) { | ||
| let [M, m, p] = fastSplit(raw, '.', 3); | ||
| let prune = 0; | ||
| while (M && preJunk.has(M.charAt(prune))) | ||
| prune++; | ||
| if (M !== undefined && prune !== 0) | ||
| M = M.substring(prune); | ||
| // the `|| !M` is so TS knows we've handled undefined | ||
| if (!M || isX(M)) | ||
| return this.#validXM(raw, m, p); | ||
| if (!m || isX(m)) | ||
| return this.#validXm(raw, M, m, p); | ||
| if (!p || isX(p)) | ||
| return this.#validXp(raw, M, m, p); | ||
| const hy = p.indexOf('-'); | ||
| const pl = p.indexOf('+'); | ||
| if (pl === -1 && hy === -1) | ||
| return this.#validTuple(raw, M, m, p); | ||
| if (pl === -1) | ||
| return this.#validXpr(raw, M, m, p, hy); | ||
| if (hy === -1) | ||
| return this.#validXbuild(raw, M, m, p, pl); | ||
| return this.#validXprbuild(raw, M, m, p, hy, pl); | ||
| } | ||
| #parseHyphenRange(min, max) { | ||
| const minv = this.#xInclusiveMin(min); | ||
| const maxv = this.#xInclusiveMax(max); | ||
| const minAny = isAny(minv); | ||
| const maxAny = isAny(maxv); | ||
| if (minAny && maxAny) | ||
| this.tuples.push(this.#getComparatorAny()); | ||
| else if (minAny) | ||
| this.tuples.push(maxv); | ||
| else if (maxAny) | ||
| this.tuples.push(minv); | ||
| else | ||
| this.tuples.push(minv, maxv); | ||
| } | ||
| #parse(comp) { | ||
| const first = comp.charAt(0); | ||
| const first2 = comp.substring(0, 2); | ||
| const v1 = comp.substring(1); | ||
| const v2 = comp.substring(2); | ||
| switch (first2) { | ||
| case '~>': | ||
| assertVersion(v2, comp); | ||
| return this.#parseTilde(v2); | ||
| case '>=': | ||
| assertVersion(v2, comp); | ||
| return this.tuples.push(this.#xInclusiveMin(v2)); | ||
| case '<=': | ||
| assertVersion(v2, comp); | ||
| return this.tuples.push(this.#xInclusiveMax(v2)); | ||
| } | ||
| switch (first) { | ||
| case '~': | ||
| assertVersion(v1, comp); | ||
| return this.#parseTilde(v1); | ||
| case '^': | ||
| assertVersion(v1, comp); | ||
| return this.#parseCaret(v1); | ||
| case '>': | ||
| assertVersion(v1, comp); | ||
| return this.tuples.push(this.#xExclusiveMin(v1)); | ||
| case '<': | ||
| assertVersion(v1, comp); | ||
| return this.tuples.push(this.#xExclusiveMax(v1)); | ||
| } | ||
| return this.#parseEq(comp); | ||
| } | ||
| #parseTilde(comp) { | ||
| const parsed = this.#parseX(comp); | ||
| if (isXMajor(parsed)) { | ||
| this.tuples.push(this.#getComparatorAny()); | ||
| return; | ||
| } | ||
| const z = this.includePrerelease ? '0' : undefined; | ||
| if (isXMinor(parsed)) { | ||
| const [M] = parsed; | ||
| this.tuples.push(['>=', new Version(comp, M, 0, 0, z, undefined)], ['<', new Version(comp, M + 1, 0, 0, '0', undefined)]); | ||
| return; | ||
| } | ||
| if (isXPatch(parsed)) { | ||
| const [M, m] = parsed; | ||
| const z = this.includePrerelease ? '0' : undefined; | ||
| this.tuples.push(['>=', new Version(comp, M, m, 0, z, undefined)], ['<', new Version(comp, M, m + 1, 0, '0', undefined)]); | ||
| return; | ||
| } | ||
| const [M, m, p, pr = z, build] = parsed; | ||
| this.tuples.push(['>=', new Version(comp, M, m, p, pr, build)], ['<', new Version(comp, M, m + 1, 0, '0', build)]); | ||
| } | ||
| #parseCaret(comp) { | ||
| const min = this.#xInclusiveMin(comp); | ||
| if (isAny(min)) { | ||
| this.tuples.push(min); | ||
| return; | ||
| } | ||
| const minv = min[1]; | ||
| if (minv.major !== 0) { | ||
| this.tuples.push(min, [ | ||
| '<', | ||
| new Version(comp, minv.major + 1, 0, 0, '0', undefined), | ||
| ]); | ||
| } | ||
| else if (minv.minor !== 0) { | ||
| this.tuples.push(min, [ | ||
| '<', | ||
| new Version(comp, minv.major, minv.minor + 1, 0, '0', undefined), | ||
| ]); | ||
| } | ||
| else if (!minv.prerelease?.length) { | ||
| this.tuples.push(['', minv]); | ||
| } | ||
| else { | ||
| this.tuples.push(min, [ | ||
| '<', | ||
| new Version(comp, minv.major, minv.minor, minv.patch + 1, '0', undefined), | ||
| ]); | ||
| } | ||
| } | ||
| #parseEq(comp) { | ||
| const parsed = this.#parseX(comp); | ||
| const z = this.includePrerelease ? '0' : undefined; | ||
| if (isFullVersion(parsed)) { | ||
| this.tuples.push(['', new Version(comp, ...parsed)]); | ||
| } | ||
| else if (isXMajor(parsed)) { | ||
| this.tuples.push(this.#getComparatorAny()); | ||
| } | ||
| else if (isXMinor(parsed)) { | ||
| this.tuples.push([ | ||
| '>=', | ||
| new Version(comp, parsed[MAJOR], 0, 0, z, undefined), | ||
| ]); | ||
| this.tuples.push([ | ||
| '<', | ||
| new Version(comp, parsed[MAJOR] + 1, 0, 0, '0', undefined), | ||
| ]); | ||
| } | ||
| else if (isXPatch(parsed)) { | ||
| this.tuples.push([ | ||
| '>=', | ||
| new Version(comp, parsed[MAJOR], parsed[MINOR], 0, z, undefined), | ||
| ], [ | ||
| '<', | ||
| new Version(comp, parsed[MAJOR], parsed[MINOR] + 1, 0, '0', undefined), | ||
| ]); | ||
| } | ||
| } | ||
| /** return true if the version is a match for this comparator */ | ||
| test(v) { | ||
| if (this.isNone) | ||
| return false; | ||
| const ip = this.includePrerelease; | ||
| const hasPR = !!v.prerelease?.length; | ||
| let prOK = ip || !hasPR; | ||
| for (const c of this.tuples) { | ||
| if (isAny(c)) { | ||
| continue; | ||
| } | ||
| const [op, cv] = c; | ||
| prOK ||= !!cv.prerelease?.length && v.tupleEquals(cv); | ||
| switch (op) { | ||
| case '': | ||
| if (!v.equals(cv)) | ||
| return false; | ||
| continue; | ||
| case '>': | ||
| if (!v.greaterThan(cv)) | ||
| return false; | ||
| continue; | ||
| case '>=': | ||
| if (!v.greaterThanEqual(cv)) | ||
| return false; | ||
| continue; | ||
| case '<': | ||
| if (!v.lessThan(cv)) | ||
| return false; | ||
| continue; | ||
| case '<=': | ||
| if (!v.lessThanEqual(cv)) | ||
| return false; | ||
| continue; | ||
| } | ||
| } | ||
| // they all passed, so it can only fail for having a prerelease | ||
| // if we allow prereleases, or saw a matching tuple, that's ok. | ||
| return prOK; | ||
| } | ||
| #getComparatorAny() { | ||
| return this.includePrerelease ? comparatorAnyPR : comparatorAny; | ||
| } | ||
| } | ||
| const isAny = (c) => c === comparatorAny || c === comparatorAnyPR; | ||
| const comparatorAny = { | ||
| isAny: true, | ||
| toString: () => '*', | ||
| includePrerelease: false, | ||
| test: (v) => !v.prerelease?.length, | ||
| }; | ||
| const comparatorAnyPR = { | ||
| isAny: true, | ||
| toString: () => '*', | ||
| includePrerelease: true, | ||
| test: (_) => true, | ||
| }; | ||
| const comparatorNone = { | ||
| isNone: true, | ||
| toString: () => '<0.0.0-0', | ||
| includePrerelease: false, | ||
| test: (_) => false, | ||
| }; |
+203
| import { Range } from './range.ts'; | ||
| import { Version } from './version.ts'; | ||
| import type { IncrementType } from './version.ts'; | ||
| export * from './comparator.ts'; | ||
| export * from './range.ts'; | ||
| export * from './version.ts'; | ||
| /** Return the parsed version string, or `undefined` if invalid */ | ||
| export declare const parse: (version: Version | string) => Version | undefined; | ||
| /** Return the parsed version range, or `undefined` if invalid */ | ||
| export declare const parseRange: (range: Range | string, includePrerelease?: boolean) => Range | undefined; | ||
| /** | ||
| * return true if the version is valid | ||
| * | ||
| * Note: do not use this if you intend to immediately parse the version if it's | ||
| * valid. Just use {@link parse}, and guard the possible undefined value, or | ||
| * use `Version.parse(..)` to throw on invalid values. | ||
| */ | ||
| export declare const valid: (version: Version | string) => boolean; | ||
| /** | ||
| * return true if the range is valid | ||
| * | ||
| * Note: do not use this if you intend to immediately parse the range if it's | ||
| * valid. Just use {@link parseRange}, and guard the possible undefined value, | ||
| * or use `new Range(..)` to throw on invalid values. | ||
| */ | ||
| export declare const validRange: (range: Range | string) => boolean; | ||
| /** | ||
| * Return true if the version satisfies the range. | ||
| */ | ||
| export declare const satisfies: (version: Version | string, range: Range | string, includePrerelease?: boolean) => boolean; | ||
| /** | ||
| * Increment the specified part of the version, and return the resulting | ||
| * object. If a Version object is provided, it will be modified in-place. | ||
| * | ||
| * See {@link Version.inc} for full description. | ||
| */ | ||
| export declare const inc: (version: Version | string, part: IncrementType, prereleaseIdentifier?: string) => Version; | ||
| /** | ||
| * The method used by {@link sort}, exported for passing directly to | ||
| * `Array.sort`. | ||
| * | ||
| * Usage: | ||
| * | ||
| * ```ts | ||
| * import { sortMethod } from '@vltpkg/semver' | ||
| * const versions = ['1.2.3', '5.2.3', '2.3.4'] | ||
| * console.log(versions.sort(sortMethod)) | ||
| * // ['1.2.3', '2.3.4', '5.2.3'] | ||
| * ``` | ||
| */ | ||
| export declare const sortMethod: (a: Version | string, b: Version | string) => number; | ||
| /** | ||
| * Sort an array of version strings or objects in ascending SemVer precedence | ||
| * order (ie, lowest versions first). | ||
| * | ||
| * Invalid version strings are sorted to the end of the array in ascending | ||
| * alphabetical order. | ||
| * | ||
| * Note: when using this method, the list is cloned prior to sorting, to | ||
| * prevent surprising mutation. To sort the list in place, see | ||
| * {@link sortMethod}. | ||
| */ | ||
| export declare const sort: <T extends Version | string = string | Version>(list: T[]) => T[]; | ||
| /** | ||
| * Sort an array of version strings or objects in descending SemVer | ||
| * precedence order (ie, highest versions first). | ||
| * | ||
| * Invalid version strings are sorted to the end of the array in ascending | ||
| * alphabetical order. | ||
| * | ||
| * Note: when using this method, the list is cloned prior to sorting, to | ||
| * prevent surprising mutation. To sort the list in place, see | ||
| * {@link rsortMethod}. | ||
| */ | ||
| export declare const rsort: <T extends Version | string = string | Version>(list: T[]) => T[]; | ||
| /** | ||
| * The method used by {@link rsort}, exported for passing directly to | ||
| * `Array.sort`. | ||
| * | ||
| * Usage: | ||
| * | ||
| * ```ts | ||
| * import { rsortMethod } from '@vltpkg/semver' | ||
| * const versions = ['1.2.3', '5.2.3', '2.3.4'] | ||
| * console.log(versions.sort(rsortMethod)) | ||
| * // ['5.2.3', '2.3.4', '1.2.3'] | ||
| * ``` | ||
| */ | ||
| export declare const rsortMethod: (a: Version | string, b: Version | string) => number; | ||
| /** | ||
| * Method used by {@link filter}, for use in `Array.filter` directly. | ||
| * | ||
| * Usage: | ||
| * | ||
| * ```ts | ||
| * import { filterMethod } from '@vltpkg/semver' | ||
| * const versions = ['1.2.3', '5.2.3', '2.3.4'] | ||
| * console.log(versions.filter(filterMethod('>=2.x'))) | ||
| * // ['5.2.3', '2.3.4'] | ||
| * ``` | ||
| */ | ||
| export declare const filterMethod: (range: Range | string, includePrerelease?: boolean) => ((version: Version | string) => boolean); | ||
| /** | ||
| * Filter a list of versions to find all that match a given range. | ||
| */ | ||
| export declare const filter: <T extends Version | string = string | Version>(list: T[], range: Range | string, includePrerelease?: boolean) => T[]; | ||
| /** | ||
| * Find the highest-precedence match for a range within a list of versions | ||
| * | ||
| * Returns `undefined` if no match was found. | ||
| */ | ||
| export declare const highest: (list: (Version | string)[], range: Range | string, includePrerelease?: boolean) => Version | undefined; | ||
| /** | ||
| * Faster form of {@link highest}, for use when the list is sorted | ||
| * in precedence order (lower-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export declare const sortedHighest: (list: (Version | string)[], range: Range | string, includePrerelease?: boolean) => Version | undefined; | ||
| /** | ||
| * Faster form of {@link highest}, for use when the list is sorted | ||
| * in reverse precedence order (higher-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export declare const rsortedHighest: (list: (Version | string)[], range: Range | string, includePrerelease?: boolean) => Version | undefined; | ||
| /** | ||
| * Find the lowest-precedence match for a range within a list of versions | ||
| * | ||
| * Returns `undefined` if no match was found. | ||
| */ | ||
| export declare const lowest: (list: (Version | string)[], range: Range | string, includePrerelease?: boolean) => Version | undefined; | ||
| /** | ||
| * Faster form of {@link lowest}, for use when the list is sorted | ||
| * in precedence order (lower-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export declare const sortedLowest: (list: (Version | string)[], range: Range | string, includePrerelease?: boolean) => Version | undefined; | ||
| /** | ||
| * Faster form of {@link lowest}, for use when the list is sorted | ||
| * in reverse precedence order (higher-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export declare const rsortedLowest: (list: (Version | string)[], range: Range | string, includePrerelease?: boolean) => Version | undefined; | ||
| /** | ||
| * Same as {@link sortMethod}, but throws if either version is not valid. | ||
| * 1 if versionA is higher precedence than versionB | ||
| * -1 if versionA is lower precedence than versionB | ||
| * 0 if they have equal precedence | ||
| */ | ||
| export declare const compare: (versionA: Version | string, versionB: Version | string) => 0 | 1 | -1; | ||
| /** | ||
| * Inverse of {@link compare} | ||
| * | ||
| * Same as {@link rsortMethod}, but throws if either version is not valid. | ||
| * | ||
| * -1 if versionA is higher precedence than versionB | ||
| * 1 if versionA is lower precedence than versionB | ||
| * 0 if they have equal precedence | ||
| */ | ||
| export declare const rcompare: (versionA: Version | string, versionB: Version | string) => 0 | 1 | -1; | ||
| /** true if versionA is > versionB. throws on invalid values */ | ||
| export declare const gt: (versionA: Version | string, versionB: Version | string) => boolean; | ||
| /** true if versionA is >= versionB. throws on invalid values */ | ||
| export declare const gte: (versionA: Version | string, versionB: Version | string) => boolean; | ||
| /** true if versionA is < versionB. throws on invalid values */ | ||
| export declare const lt: (versionA: Version | string, versionB: Version | string) => boolean; | ||
| /** true if versionA is <= versionB. throws on invalid values */ | ||
| export declare const lte: (versionA: Version | string, versionB: Version | string) => boolean; | ||
| /** true if versionA is not equal to versionB. throws on invalid values */ | ||
| export declare const neq: (versionA: Version | string, versionB: Version | string) => boolean; | ||
| /** true if versionA is equal to versionB. throws on invalid values */ | ||
| export declare const eq: (versionA: Version | string, versionB: Version | string) => boolean; | ||
| /** extract the major version number, or undefined if invalid */ | ||
| export declare const major: (version: Version | string) => number | undefined; | ||
| /** extract the minor version number, or undefined if invalid */ | ||
| export declare const minor: (version: Version | string) => number | undefined; | ||
| /** extract the patch version number, or undefined if invalid */ | ||
| export declare const patch: (version: Version | string) => number | undefined; | ||
| /** | ||
| * extract the list of prerelease identifiers, or undefined if the version | ||
| * is invalid. If no prerelease identifiers are present, returns `[]`. | ||
| */ | ||
| export declare const prerelease: (version: Version | string) => (string | number)[] | undefined; | ||
| /** | ||
| * extract the list of build identifiers, or undefined if the version | ||
| * is invalid. If no build identifiers are present, returns `[]`. | ||
| */ | ||
| export declare const build: (version: Version | string) => string[] | undefined; | ||
| /** return all versions that do not have any prerelease identifiers */ | ||
| export declare const stable: <T extends Version | string = string | Version>(versions: T[]) => T[]; | ||
| /** | ||
| * Return true if the range r1 intersects any of the ranges r2 | ||
| * r1 and r2 are either Range objects or range strings. | ||
| * Returns true if any version would satisfy both ranges. | ||
| */ | ||
| export declare const intersects: (r1: Range | string, r2: Range | string, includePrerelease?: boolean) => boolean; |
+457
| import { Range } from "./range.js"; | ||
| import { Version } from "./version.js"; | ||
| import { syntaxError } from '@vltpkg/error-cause'; | ||
| export * from "./comparator.js"; | ||
| export * from "./range.js"; | ||
| export * from "./version.js"; | ||
| /** Return the parsed version string, or `undefined` if invalid */ | ||
| export const parse = (version) => { | ||
| if (!(typeof version === 'string')) | ||
| return version; | ||
| try { | ||
| return Version.parse(version); | ||
| } | ||
| catch { | ||
| return undefined; | ||
| } | ||
| }; | ||
| /** Return the parsed version range, or `undefined` if invalid */ | ||
| export const parseRange = (range, includePrerelease = false) => { | ||
| if (typeof range === 'object') { | ||
| if (range.includePrerelease === includePrerelease) | ||
| return range; | ||
| range = range.raw; | ||
| } | ||
| try { | ||
| return new Range(range, includePrerelease); | ||
| } | ||
| catch { | ||
| return undefined; | ||
| } | ||
| }; | ||
| /** | ||
| * return true if the version is valid | ||
| * | ||
| * Note: do not use this if you intend to immediately parse the version if it's | ||
| * valid. Just use {@link parse}, and guard the possible undefined value, or | ||
| * use `Version.parse(..)` to throw on invalid values. | ||
| */ | ||
| export const valid = (version) => !!parse(version); | ||
| /** | ||
| * return true if the range is valid | ||
| * | ||
| * Note: do not use this if you intend to immediately parse the range if it's | ||
| * valid. Just use {@link parseRange}, and guard the possible undefined value, | ||
| * or use `new Range(..)` to throw on invalid values. | ||
| */ | ||
| export const validRange = (range) => !!parseRange(range); | ||
| /** | ||
| * Return true if the version satisfies the range. | ||
| */ | ||
| export const satisfies = (version, range, includePrerelease = false) => { | ||
| if (typeof version === 'string') { | ||
| const parsed = parse(version); | ||
| if (!parsed) | ||
| return false; | ||
| version = parsed; | ||
| } | ||
| if (typeof range === 'string') { | ||
| const parsed = parseRange(range, includePrerelease); | ||
| if (!parsed) | ||
| return false; | ||
| range = parsed; | ||
| } | ||
| return version.satisfies(range); | ||
| }; | ||
| /** | ||
| * Increment the specified part of the version, and return the resulting | ||
| * object. If a Version object is provided, it will be modified in-place. | ||
| * | ||
| * See {@link Version.inc} for full description. | ||
| */ | ||
| export const inc = (version, part, prereleaseIdentifier) => (typeof version === 'string' ? | ||
| Version.parse(version) | ||
| : version).inc(part, prereleaseIdentifier); | ||
| /** | ||
| * The method used by {@link sort}, exported for passing directly to | ||
| * `Array.sort`. | ||
| * | ||
| * Usage: | ||
| * | ||
| * ```ts | ||
| * import { sortMethod } from '@vltpkg/semver' | ||
| * const versions = ['1.2.3', '5.2.3', '2.3.4'] | ||
| * console.log(versions.sort(sortMethod)) | ||
| * // ['1.2.3', '2.3.4', '5.2.3'] | ||
| * ``` | ||
| */ | ||
| export const sortMethod = (a, b) => { | ||
| const pa = parse(a); | ||
| const pb = parse(b); | ||
| /* c8 ignore start - nondeterministic */ | ||
| if (!pa && !pb) | ||
| return String(a).localeCompare(String(b), 'en'); | ||
| if (!pa) | ||
| return 1; | ||
| if (!pb) | ||
| return -1; | ||
| /* c8 ignore stop */ | ||
| return pa.compare(pb); | ||
| }; | ||
| /** | ||
| * Sort an array of version strings or objects in ascending SemVer precedence | ||
| * order (ie, lowest versions first). | ||
| * | ||
| * Invalid version strings are sorted to the end of the array in ascending | ||
| * alphabetical order. | ||
| * | ||
| * Note: when using this method, the list is cloned prior to sorting, to | ||
| * prevent surprising mutation. To sort the list in place, see | ||
| * {@link sortMethod}. | ||
| */ | ||
| export const sort = (list) => list.slice().sort(sortMethod); | ||
| /** | ||
| * Sort an array of version strings or objects in descending SemVer | ||
| * precedence order (ie, highest versions first). | ||
| * | ||
| * Invalid version strings are sorted to the end of the array in ascending | ||
| * alphabetical order. | ||
| * | ||
| * Note: when using this method, the list is cloned prior to sorting, to | ||
| * prevent surprising mutation. To sort the list in place, see | ||
| * {@link rsortMethod}. | ||
| */ | ||
| export const rsort = (list) => list.slice().sort(rsortMethod); | ||
| /** | ||
| * The method used by {@link rsort}, exported for passing directly to | ||
| * `Array.sort`. | ||
| * | ||
| * Usage: | ||
| * | ||
| * ```ts | ||
| * import { rsortMethod } from '@vltpkg/semver' | ||
| * const versions = ['1.2.3', '5.2.3', '2.3.4'] | ||
| * console.log(versions.sort(rsortMethod)) | ||
| * // ['5.2.3', '2.3.4', '1.2.3'] | ||
| * ``` | ||
| */ | ||
| export const rsortMethod = (a, b) => { | ||
| const pa = parse(a); | ||
| const pb = parse(b); | ||
| /* c8 ignore start - nondeterministic */ | ||
| if (!pa && !pb) | ||
| return String(a).localeCompare(String(b), 'en'); | ||
| if (!pa) | ||
| return 1; | ||
| if (!pb) | ||
| return -1; | ||
| /* c8 ignore stop */ | ||
| return pa.rcompare(pb); | ||
| }; | ||
| /** | ||
| * Method used by {@link filter}, for use in `Array.filter` directly. | ||
| * | ||
| * Usage: | ||
| * | ||
| * ```ts | ||
| * import { filterMethod } from '@vltpkg/semver' | ||
| * const versions = ['1.2.3', '5.2.3', '2.3.4'] | ||
| * console.log(versions.filter(filterMethod('>=2.x'))) | ||
| * // ['5.2.3', '2.3.4'] | ||
| * ``` | ||
| */ | ||
| export const filterMethod = (range, includePrerelease = false) => { | ||
| const r = parseRange(range, includePrerelease); | ||
| return !r ? | ||
| () => false | ||
| : version => satisfies(version, r, r.includePrerelease); | ||
| }; | ||
| /** | ||
| * Filter a list of versions to find all that match a given range. | ||
| */ | ||
| export const filter = (list, range, includePrerelease = false) => list.filter(filterMethod(range, includePrerelease)); | ||
| /** | ||
| * Find the highest-precedence match for a range within a list of versions | ||
| * | ||
| * Returns `undefined` if no match was found. | ||
| */ | ||
| export const highest = (list, range, includePrerelease = false) => { | ||
| const r = parseRange(range, includePrerelease); | ||
| if (!r) | ||
| return undefined; | ||
| let max = undefined; | ||
| for (const v of list) { | ||
| const version = parse(v); | ||
| if (!version) | ||
| continue; | ||
| if (!version.satisfies(r)) | ||
| continue; | ||
| if (!max) | ||
| max = version; | ||
| else if (version.greaterThan(max)) | ||
| max = version; | ||
| } | ||
| return max; | ||
| }; | ||
| /** | ||
| * Faster form of {@link highest}, for use when the list is sorted | ||
| * in precedence order (lower-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export const sortedHighest = (list, range, includePrerelease = false) => { | ||
| const r = parseRange(range, includePrerelease); | ||
| if (!r) | ||
| return undefined; | ||
| for (let i = list.length - 1; i >= 0; i--) { | ||
| const v = list[i]; | ||
| /* c8 ignore next */ | ||
| if (!v) | ||
| continue; | ||
| const version = parse(v); | ||
| if (!version) | ||
| continue; | ||
| if (!version.satisfies(r)) | ||
| continue; | ||
| return version; | ||
| } | ||
| }; | ||
| /** | ||
| * Faster form of {@link highest}, for use when the list is sorted | ||
| * in reverse precedence order (higher-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export const rsortedHighest = (list, range, includePrerelease = false) => { | ||
| const r = parseRange(range, includePrerelease); | ||
| if (!r) | ||
| return undefined; | ||
| for (const v of list) { | ||
| const version = parse(v); | ||
| if (!version) | ||
| continue; | ||
| if (!version.satisfies(r)) | ||
| continue; | ||
| return version; | ||
| } | ||
| }; | ||
| /** | ||
| * Find the lowest-precedence match for a range within a list of versions | ||
| * | ||
| * Returns `undefined` if no match was found. | ||
| */ | ||
| export const lowest = (list, range, includePrerelease = false) => { | ||
| const r = parseRange(range, includePrerelease); | ||
| if (!r) | ||
| return undefined; | ||
| let min = undefined; | ||
| for (const v of list) { | ||
| const version = parse(v); | ||
| if (!version) | ||
| continue; | ||
| if (!version.satisfies(r)) | ||
| continue; | ||
| if (!min) | ||
| min = version; | ||
| else if (version.lessThan(min)) | ||
| min = version; | ||
| } | ||
| return min; | ||
| }; | ||
| /** | ||
| * Faster form of {@link lowest}, for use when the list is sorted | ||
| * in precedence order (lower-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export const sortedLowest = (list, range, includePrerelease = false) => rsortedHighest(list, range, includePrerelease); | ||
| /** | ||
| * Faster form of {@link lowest}, for use when the list is sorted | ||
| * in reverse precedence order (higher-precedence versions first). | ||
| * | ||
| * Note: This stops at the first match, and will produce incorrect results | ||
| * when the list is not properly sorted! | ||
| */ | ||
| export const rsortedLowest = (list, range, includePrerelease = false) => sortedHighest(list, range, includePrerelease); | ||
| /** | ||
| * Same as {@link sortMethod}, but throws if either version is not valid. | ||
| * 1 if versionA is higher precedence than versionB | ||
| * -1 if versionA is lower precedence than versionB | ||
| * 0 if they have equal precedence | ||
| */ | ||
| export const compare = (versionA, versionB) => { | ||
| const a = parse(versionA); | ||
| if (!a) { | ||
| throw syntaxError('invalid version', { found: versionA }); | ||
| } | ||
| const b = parse(versionB); | ||
| if (!b) { | ||
| throw syntaxError('invalid version', { found: versionB }); | ||
| } | ||
| return a.compare(b); | ||
| }; | ||
| /** | ||
| * Inverse of {@link compare} | ||
| * | ||
| * Same as {@link rsortMethod}, but throws if either version is not valid. | ||
| * | ||
| * -1 if versionA is higher precedence than versionB | ||
| * 1 if versionA is lower precedence than versionB | ||
| * 0 if they have equal precedence | ||
| */ | ||
| export const rcompare = (versionA, versionB) => compare(versionB, versionA); | ||
| /** true if versionA is > versionB. throws on invalid values */ | ||
| export const gt = (versionA, versionB) => compare(versionA, versionB) > 0; | ||
| /** true if versionA is >= versionB. throws on invalid values */ | ||
| export const gte = (versionA, versionB) => compare(versionA, versionB) >= 0; | ||
| /** true if versionA is < versionB. throws on invalid values */ | ||
| export const lt = (versionA, versionB) => compare(versionA, versionB) < 0; | ||
| /** true if versionA is <= versionB. throws on invalid values */ | ||
| export const lte = (versionA, versionB) => compare(versionA, versionB) <= 0; | ||
| /** true if versionA is not equal to versionB. throws on invalid values */ | ||
| export const neq = (versionA, versionB) => compare(versionA, versionB) !== 0; | ||
| /** true if versionA is equal to versionB. throws on invalid values */ | ||
| export const eq = (versionA, versionB) => compare(versionA, versionB) === 0; | ||
| /** extract the major version number, or undefined if invalid */ | ||
| export const major = (version) => parse(version)?.major; | ||
| /** extract the minor version number, or undefined if invalid */ | ||
| export const minor = (version) => parse(version)?.minor; | ||
| /** extract the patch version number, or undefined if invalid */ | ||
| export const patch = (version) => parse(version)?.patch; | ||
| /** | ||
| * extract the list of prerelease identifiers, or undefined if the version | ||
| * is invalid. If no prerelease identifiers are present, returns `[]`. | ||
| */ | ||
| export const prerelease = (version) => { | ||
| const p = parse(version); | ||
| if (!p) | ||
| return undefined; | ||
| return p.prerelease ?? []; | ||
| }; | ||
| /** | ||
| * extract the list of build identifiers, or undefined if the version | ||
| * is invalid. If no build identifiers are present, returns `[]`. | ||
| */ | ||
| export const build = (version) => { | ||
| const p = parse(version); | ||
| if (!p) | ||
| return undefined; | ||
| return p.build ?? []; | ||
| }; | ||
| /** return all versions that do not have any prerelease identifiers */ | ||
| export const stable = (versions) => versions.filter(v => { | ||
| const p = parse(v); | ||
| if (!p) | ||
| return false; | ||
| return !p.prerelease?.length; | ||
| }); | ||
| /** | ||
| * Return true if the range r1 intersects any of the ranges r2 | ||
| * r1 and r2 are either Range objects or range strings. | ||
| * Returns true if any version would satisfy both ranges. | ||
| */ | ||
| export const intersects = (r1, r2, includePrerelease) => { | ||
| const range1 = typeof r1 === 'string' ? parseRange(r1, includePrerelease) : r1; | ||
| const range2 = typeof r2 === 'string' ? parseRange(r2, includePrerelease) : r2; | ||
| if (!range1 || !range2) | ||
| return false; | ||
| // If either range is 'any', they intersect | ||
| if (range1.isAny || range2.isAny) | ||
| return true; | ||
| // Check if any set from range1 intersects with any set from range2 | ||
| return range1.set.some(set1 => range2.set.some(set2 => intersectComparators(set1, set2))); | ||
| }; | ||
| /** | ||
| * Check if two comparators can be satisfied simultaneously | ||
| */ | ||
| const intersectComparators = (comp1, comp2) => { | ||
| // Collect all tuples from both comparators | ||
| const tuples1 = comp1.tuples.filter((t) => Array.isArray(t)); | ||
| const tuples2 = comp2.tuples.filter((t) => Array.isArray(t)); | ||
| // Check if there's a satisfiable combination | ||
| return satisfiableRange(tuples1.concat(tuples2)); | ||
| }; | ||
| /** | ||
| * Check if a set of operator-version tuples represents a satisfiable range | ||
| * This is a simplified implementation that handles the most common cases | ||
| */ | ||
| const satisfiableRange = (tuples) => { | ||
| // Find bounds | ||
| let lowerBound = null; | ||
| let lowerInclusive = false; | ||
| let upperBound = null; | ||
| let upperInclusive = false; | ||
| let hasExact = null; | ||
| for (const [op, ver] of tuples) { | ||
| switch (op) { | ||
| case '': | ||
| // Exact match - if we already have a different exact match, no intersection | ||
| if (hasExact && !eq(hasExact, ver)) | ||
| return false; | ||
| hasExact = ver; | ||
| break; | ||
| case '>=': | ||
| /* c8 ignore start */ | ||
| if (!lowerBound || | ||
| gt(ver, lowerBound) || | ||
| (eq(ver, lowerBound) && !lowerInclusive)) { | ||
| lowerBound = ver; | ||
| lowerInclusive = true; | ||
| } | ||
| /* c8 ignore stop */ | ||
| break; | ||
| case '>': | ||
| if (!lowerBound || gt(ver, lowerBound)) { | ||
| lowerBound = ver; | ||
| lowerInclusive = false; | ||
| } | ||
| break; | ||
| case '<=': | ||
| if (!upperBound || lt(ver, upperBound)) { | ||
| upperBound = ver; | ||
| upperInclusive = true; | ||
| } | ||
| break; | ||
| case '<': | ||
| /* c8 ignore start */ | ||
| if (!upperBound || | ||
| lt(ver, upperBound) || | ||
| eq(ver, upperBound)) { | ||
| upperBound = ver; | ||
| upperInclusive = false; | ||
| } | ||
| /* c8 ignore stop */ | ||
| break; | ||
| } | ||
| } | ||
| // If we have an exact match, check if it's within bounds | ||
| if (hasExact) { | ||
| if (lowerBound) { | ||
| if (lowerInclusive ? | ||
| lt(hasExact, lowerBound) | ||
| : lte(hasExact, lowerBound)) { | ||
| return false; | ||
| } | ||
| } | ||
| if (upperBound) { | ||
| if (upperInclusive ? | ||
| gt(hasExact, upperBound) | ||
| : gte(hasExact, upperBound)) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| // Check if lower bound is less than or equal to upper bound | ||
| if (lowerBound && upperBound) { | ||
| if (gt(lowerBound, upperBound)) | ||
| return false; | ||
| if (eq(lowerBound, upperBound) && | ||
| !(lowerInclusive && upperInclusive)) | ||
| return false; | ||
| } | ||
| return true; | ||
| }; |
| import { Comparator } from './comparator.ts'; | ||
| import type { Version } from './version.ts'; | ||
| export declare const isRange: (range: unknown) => range is Range; | ||
| /** | ||
| * A representation of a semver range, used to test versions. | ||
| * | ||
| * Includes a set of comparators representing the `||`-separated | ||
| * sections of the range string | ||
| */ | ||
| export declare class Range { | ||
| #private; | ||
| /** raw string used to create this Range */ | ||
| raw: string; | ||
| /** true if the range is `*` */ | ||
| isAny: boolean; | ||
| /** true if the range is a single semver version */ | ||
| isSingle: boolean; | ||
| /** true if the range cannot match anything */ | ||
| /** | ||
| * set of {@link Comparator} objects representing the `||`-separated sections | ||
| * of the range. If at least one of these matches, then the version is a | ||
| * match. | ||
| */ | ||
| set: Comparator[]; | ||
| /** true if all prerelease versions should be included */ | ||
| includePrerelease: boolean; | ||
| constructor(range: string, includePrerelease?: boolean); | ||
| /** | ||
| * test a {@link Version} against the range | ||
| */ | ||
| test(v: Version): boolean; | ||
| /** return the simplified canonical form of this range */ | ||
| toString(): string; | ||
| } |
+101
| import { fastSplit } from '@vltpkg/fast-split'; | ||
| import { Comparator } from "./comparator.js"; | ||
| import { asError } from '@vltpkg/types'; | ||
| export const isRange = (range) => { | ||
| return (range instanceof Range || | ||
| (typeof range === 'object' && | ||
| range !== null && | ||
| 'raw' in range && | ||
| typeof range.raw === 'string' && | ||
| 'set' in range && | ||
| Array.isArray(range.set) && | ||
| range.set.every(c => c instanceof Comparator))); | ||
| }; | ||
| /** | ||
| * A representation of a semver range, used to test versions. | ||
| * | ||
| * Includes a set of comparators representing the `||`-separated | ||
| * sections of the range string | ||
| */ | ||
| export class Range { | ||
| /** raw string used to create this Range */ | ||
| raw; | ||
| /** true if the range is `*` */ | ||
| isAny; | ||
| /** true if the range is a single semver version */ | ||
| isSingle; | ||
| /** true if the range cannot match anything */ | ||
| /** | ||
| * set of {@link Comparator} objects representing the `||`-separated sections | ||
| * of the range. If at least one of these matches, then the version is a | ||
| * match. | ||
| */ | ||
| set = []; | ||
| /** true if all prerelease versions should be included */ | ||
| includePrerelease; | ||
| /** cached toString */ | ||
| #toString; | ||
| constructor(range, includePrerelease = false) { | ||
| this.raw = range; | ||
| this.includePrerelease = includePrerelease; | ||
| this.isAny = false; | ||
| let isFirst = true; | ||
| this.isSingle = false; | ||
| const comparatorErrors = []; | ||
| fastSplit(range, '||', -1, part => { | ||
| if (this.isAny) | ||
| return; | ||
| const cmp = this.#maybeComparator(part, this.includePrerelease); | ||
| if (cmp instanceof Error) { | ||
| comparatorErrors.push(cmp); | ||
| return; | ||
| } | ||
| if (cmp.isAny) { | ||
| this.set = [cmp]; | ||
| this.isAny = true; | ||
| return; | ||
| } | ||
| this.set.push(cmp); | ||
| if (!isFirst) | ||
| this.isSingle = false; | ||
| else if (Array.isArray(cmp.tuples) && | ||
| cmp.tuples.length === 1 && | ||
| Array.isArray(cmp.tuples[0]) && | ||
| cmp.tuples[0][0] === '') { | ||
| this.isSingle = true; | ||
| } | ||
| isFirst = false; | ||
| }); | ||
| if (!this.set.length && comparatorErrors.length) { | ||
| if (comparatorErrors.length === 1 && comparatorErrors[0]) { | ||
| throw comparatorErrors[0]; | ||
| } | ||
| throw new AggregateError(comparatorErrors); | ||
| } | ||
| } | ||
| #maybeComparator(part, includePrerelease) { | ||
| try { | ||
| return new Comparator(part, includePrerelease); | ||
| } | ||
| catch (er) { | ||
| return asError(er); | ||
| } | ||
| } | ||
| /** | ||
| * test a {@link Version} against the range | ||
| */ | ||
| test(v) { | ||
| return this.set.some(c => c.test(v)); | ||
| } | ||
| /** return the simplified canonical form of this range */ | ||
| toString() { | ||
| if (this.#toString) | ||
| return this.#toString; | ||
| if (this.isSingle) { | ||
| this.#toString = String(this.set[0]); | ||
| return this.#toString; | ||
| } | ||
| this.#toString = this.set.map(c => String(c)).join(' || '); | ||
| return this.#toString; | ||
| } | ||
| } |
| import type { Range } from './range.ts'; | ||
| /** | ||
| * Values of valid increment types. | ||
| */ | ||
| export declare const versionIncrements: readonly ["major", "minor", "patch", "pre", "premajor", "preminor", "prepatch", "prerelease"]; | ||
| /** | ||
| * Types of incrementing supported by {@link Version#inc} | ||
| */ | ||
| export type IncrementType = (typeof versionIncrements)[number]; | ||
| /** | ||
| * A parsed object representation of a SemVer version string | ||
| * | ||
| * This is a bit less forgiving than node-semver, in that prerelease versions | ||
| * MUST start with '-'. Otherwise, the allowed syntax is identical. | ||
| */ | ||
| export declare class Version { | ||
| /** raw string provided to create this Version */ | ||
| raw: string; | ||
| /** major version number */ | ||
| major: number; | ||
| /** minor version number */ | ||
| minor: number; | ||
| /** patch version number */ | ||
| patch: number; | ||
| /** | ||
| * List of `'.'`-separated strings and numbers indicating that this | ||
| * version is a prerelease. | ||
| * | ||
| * This is undefined if the version does not have a prerelease section. | ||
| */ | ||
| prerelease?: (number | string)[]; | ||
| /** | ||
| * List of `'.'`-separated strings in the `build` section. | ||
| * | ||
| * This is undefined if the version does not have a build. | ||
| */ | ||
| build?: string[]; | ||
| /** Canonical strict form of this version */ | ||
| toString(): string; | ||
| /** Generate a `Version` object from a SemVer string */ | ||
| static parse(version: string): Version; | ||
| constructor(version: string, major: number, minor: number, patch: number, prerelease: string | undefined, build: string | undefined); | ||
| /** | ||
| * Return 1 if this is > the provided version, -1 if we're less, or 0 if | ||
| * they are equal. | ||
| * | ||
| * No special handling for prerelease versions, this is just a precedence | ||
| * comparison. | ||
| * | ||
| * This can be used to sort a list of versions by precedence: | ||
| * | ||
| * ```ts | ||
| * const versions: Version[] = getVersionsSomehow() | ||
| * const sorted = versions.sort((a, b) => a.compare(b)) | ||
| * ``` | ||
| */ | ||
| compare(v: Version): -1 | 0 | 1; | ||
| /** | ||
| * The inverse of compare, for sorting version lists in reverse order | ||
| */ | ||
| rcompare(v: Version): number; | ||
| /** true if this version is > the argument */ | ||
| greaterThan(v: Version): boolean; | ||
| /** true if this version is >= the argument */ | ||
| greaterThanEqual(v: Version): boolean; | ||
| /** true if this version is < the argument */ | ||
| lessThan(v: Version): boolean; | ||
| /** true if this version is <= the argument */ | ||
| lessThanEqual(v: Version): boolean; | ||
| /** true if these two versions have equal SemVer precedence */ | ||
| equals(v: Version): boolean; | ||
| /** just compare the M.m.p parts of the version */ | ||
| tupleEquals(v: Version): boolean; | ||
| /** true if this version satisfies the range */ | ||
| satisfies(r: Range): boolean; | ||
| /** | ||
| * Increment the version in place, in the manner specified. | ||
| * | ||
| * Part behaviors: | ||
| * | ||
| * - `'major'` If the version is a `M.0.0-...` version with a prerelease, then | ||
| * simply drop the prerelease. Otherwise, set the minor and patch to 0, and | ||
| * increment the major. So `1.0.0-beta` becomes `1.0.0`, and `1.2.3` becomes | ||
| * `2.0.0` | ||
| * | ||
| * - `'minor'` If the version is a `M.m.0-...` version with a prerelease, then | ||
| * simply drop the prerelease. Otherwise, set the patch to 0, and increment the | ||
| * minor. So `1.2.0-beta` becomes `1.2.0`, and `1.2.3` becomes `1.3.0`. | ||
| * | ||
| * - `'patch'` If the version has a prerelease, then simply drop the | ||
| * prerelease. Otherwise, increment the patch value. So `1.2.3-beta` becomes | ||
| * `1.2.3` and `1.2.3` becomes `1.2.4`. | ||
| * | ||
| * - `'premajor'` Set the patch and minor versions to `0`, increment the major | ||
| * version, and add a prerelease, using the optional identifier. | ||
| * | ||
| * - `'preminor'` Set the patch version to `0`, increment the minor version, | ||
| * and add a prerelease, using the optional identifier. | ||
| * | ||
| * - `'prepatch'` If a prerelease is already present, increment the patch | ||
| * version, otherwise leave it untouched, and add a prerelease, using the | ||
| * optional identifier. | ||
| * | ||
| * - `'prerelease'` If a prerelease version is present, then behave the same as | ||
| * `'prepatch'`. Otherwise, add a prerelease, using the optional identifier. | ||
| * | ||
| * - `'pre'` This is mostly for use by the other prerelease incrementers. | ||
| * | ||
| * - If a prerelease identifier is provided: | ||
| * | ||
| * Update that named portion of the prerelease. For example, | ||
| * `inc('1.2.3-beta.4', 'pre', 'beta')` would result in `1.2.3-beta.5`. | ||
| * | ||
| * If there is no prerelease identifier by that name, then replace the | ||
| * prerelease with `[name]`. So `inc('1.2.3-alpha.4', 'pre', 'beta')` | ||
| * would result in `1.2.3-beta`. | ||
| * | ||
| * If the prerelease identifer is present, but has no numeric value | ||
| * following it, then add `0`. So `inc('1.2.3-beta', 'pre', 'beta')` | ||
| * would result in `1.2.3-beta.0`. | ||
| * | ||
| * - If no prerelease identifier is provided: | ||
| * | ||
| * If there is no current prerelease, then set the prerelease to `0`. So, | ||
| * `inc('1.2.3', 'pre')` becomes `1.2.3-0`. | ||
| * | ||
| * If the last item in the prerelease is numeric, then increment it. So, | ||
| * `inc('1.2.3-beta.3', 'pre')` becomes `1.2.3-beta.4`. | ||
| */ | ||
| inc(part: IncrementType, prereleaseIdentifier?: string): this; | ||
| } |
+366
| import { syntaxError, typeError } from '@vltpkg/error-cause'; | ||
| import { fastSplit } from '@vltpkg/fast-split'; | ||
| const maybeNumber = (s) => { | ||
| if (!/^[0-9]+$/.test(s)) | ||
| return s; | ||
| const n = Number(s); | ||
| return n <= Number.MAX_SAFE_INTEGER ? n : s; | ||
| }; | ||
| const safeNumber = (s, version, field) => { | ||
| const n = Number(s); | ||
| if (n > Number.MAX_SAFE_INTEGER) { | ||
| throw invalidVersion(version, `invalid ${field}, must be <= ${Number.MAX_SAFE_INTEGER}`); | ||
| } | ||
| return n; | ||
| }; | ||
| const re = { | ||
| prefix: /^[ v=]+/, | ||
| main: /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)/, | ||
| prerelease: /-([0-9a-zA-Z_.-]+)(?:$|\+)/, | ||
| build: /\+([0-9a-zA-Z_.-]+)$/, | ||
| full: /^[ v=]*(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9a-zA-Z_.-]+))?(?:\+([0-9a-zA-Z_.-]+))?$/, | ||
| }; | ||
| const invalidVersion = (version, message) => { | ||
| const er = syntaxError(`invalid version: ${message}`, { version }, Version); | ||
| return er; | ||
| }; | ||
| /** | ||
| * Values of valid increment types. | ||
| */ | ||
| export const versionIncrements = [ | ||
| 'major', | ||
| 'minor', | ||
| 'patch', | ||
| 'pre', | ||
| 'premajor', | ||
| 'preminor', | ||
| 'prepatch', | ||
| 'prerelease', | ||
| ]; | ||
| /** | ||
| * A parsed object representation of a SemVer version string | ||
| * | ||
| * This is a bit less forgiving than node-semver, in that prerelease versions | ||
| * MUST start with '-'. Otherwise, the allowed syntax is identical. | ||
| */ | ||
| export class Version { | ||
| /** raw string provided to create this Version */ | ||
| raw; | ||
| /** major version number */ | ||
| major; | ||
| /** minor version number */ | ||
| minor; | ||
| /** patch version number */ | ||
| patch; | ||
| /** | ||
| * List of `'.'`-separated strings and numbers indicating that this | ||
| * version is a prerelease. | ||
| * | ||
| * This is undefined if the version does not have a prerelease section. | ||
| */ | ||
| prerelease; | ||
| /** | ||
| * List of `'.'`-separated strings in the `build` section. | ||
| * | ||
| * This is undefined if the version does not have a build. | ||
| */ | ||
| build; | ||
| /** Canonical strict form of this version */ | ||
| toString() { | ||
| return `${this.major}.${this.minor}.${this.patch}${this.prerelease ? '-' + this.prerelease.join('.') : ''}${this.build ? '+' + this.build.join('.') : ''}`; | ||
| } | ||
| /** Generate a `Version` object from a SemVer string */ | ||
| static parse(version) { | ||
| version = version.replace(re.prefix, '').trim(); | ||
| if (version.length > 256) { | ||
| throw invalidVersion(version, 'must be less than 256 characters'); | ||
| } | ||
| const parsed = re.full.exec(version); | ||
| if (!parsed) { | ||
| const main = re.main.exec(version); | ||
| if (!main) { | ||
| throw invalidVersion(version, 'no Major.minor.patch tuple present'); | ||
| } | ||
| else { | ||
| throw invalidVersion(version, 'invalid build or patch section'); | ||
| } | ||
| } | ||
| const [_, major_, minor_, patch_, prerelease, build] = parsed; | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| const major = safeNumber(major_, version, 'major'); | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| const minor = safeNumber(minor_, version, 'minor'); | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| const patch = safeNumber(patch_, version, 'patch'); | ||
| return new Version(version, major, minor, patch, prerelease, build); | ||
| } | ||
| constructor(version, major, minor, patch, prerelease, build) { | ||
| this.raw = version; | ||
| this.major = major; | ||
| this.minor = minor; | ||
| this.patch = patch; | ||
| // has prerelease and/or build | ||
| if (prerelease) { | ||
| this.prerelease = fastSplit(prerelease, '.', -1, c => { | ||
| if (!c) { | ||
| throw invalidVersion(version, 'invalid prerelease, empty identifiers not allowed'); | ||
| } | ||
| return maybeNumber(c); | ||
| }); | ||
| } | ||
| if (build) { | ||
| this.build = fastSplit(build, '.', -1, c => { | ||
| if (!c) { | ||
| throw invalidVersion(version, 'invalid build metadata, empty identifiers not allowed'); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| /** | ||
| * Return 1 if this is > the provided version, -1 if we're less, or 0 if | ||
| * they are equal. | ||
| * | ||
| * No special handling for prerelease versions, this is just a precedence | ||
| * comparison. | ||
| * | ||
| * This can be used to sort a list of versions by precedence: | ||
| * | ||
| * ```ts | ||
| * const versions: Version[] = getVersionsSomehow() | ||
| * const sorted = versions.sort((a, b) => a.compare(b)) | ||
| * ``` | ||
| */ | ||
| compare(v) { | ||
| if (this.major > v.major) | ||
| return 1; | ||
| if (this.major < v.major) | ||
| return -1; | ||
| if (this.minor > v.minor) | ||
| return 1; | ||
| if (this.minor < v.minor) | ||
| return -1; | ||
| if (this.patch > v.patch) | ||
| return 1; | ||
| if (this.patch < v.patch) | ||
| return -1; | ||
| // main tuple is equal now | ||
| // if the version has no pr, we're definitely less than or equal to | ||
| if (!v.prerelease?.length) | ||
| return !this.prerelease?.length ? 0 : -1; | ||
| // v has a pr. if we don't, we're > it | ||
| if (!this.prerelease?.length) | ||
| return 1; | ||
| // we both have prereleases | ||
| const len = Math.max(this.prerelease.length, v.prerelease.length); | ||
| const me = this.prerelease; | ||
| const thee = v.prerelease; | ||
| for (let i = 0; i < len; i++) { | ||
| const m = me[i]; | ||
| const t = thee[i]; | ||
| if (m === t) | ||
| continue; | ||
| // having a field is > not having it | ||
| if (t === undefined) | ||
| return 1; | ||
| if (m === undefined) | ||
| return -1; | ||
| // string parts are higher precedence than | ||
| if (typeof m !== typeof t) { | ||
| return typeof m === 'string' ? 1 : -1; | ||
| } | ||
| return m > t ? 1 : -1; | ||
| } | ||
| return 0; | ||
| } | ||
| /** | ||
| * The inverse of compare, for sorting version lists in reverse order | ||
| */ | ||
| rcompare(v) { | ||
| return -1 * this.compare(v); | ||
| } | ||
| /** true if this version is > the argument */ | ||
| greaterThan(v) { | ||
| return this.compare(v) === 1; | ||
| } | ||
| /** true if this version is >= the argument */ | ||
| greaterThanEqual(v) { | ||
| return this.compare(v) > -1; | ||
| } | ||
| /** true if this version is < the argument */ | ||
| lessThan(v) { | ||
| return this.compare(v) === -1; | ||
| } | ||
| /** true if this version is <= the argument */ | ||
| lessThanEqual(v) { | ||
| return this.compare(v) < 1; | ||
| } | ||
| /** true if these two versions have equal SemVer precedence */ | ||
| equals(v) { | ||
| return this.compare(v) === 0; | ||
| } | ||
| /** just compare the M.m.p parts of the version */ | ||
| tupleEquals(v) { | ||
| return (this.major === v.major && | ||
| this.minor === v.minor && | ||
| this.patch === v.patch); | ||
| } | ||
| /** true if this version satisfies the range */ | ||
| satisfies(r) { | ||
| return r.test(this); | ||
| } | ||
| /** | ||
| * Increment the version in place, in the manner specified. | ||
| * | ||
| * Part behaviors: | ||
| * | ||
| * - `'major'` If the version is a `M.0.0-...` version with a prerelease, then | ||
| * simply drop the prerelease. Otherwise, set the minor and patch to 0, and | ||
| * increment the major. So `1.0.0-beta` becomes `1.0.0`, and `1.2.3` becomes | ||
| * `2.0.0` | ||
| * | ||
| * - `'minor'` If the version is a `M.m.0-...` version with a prerelease, then | ||
| * simply drop the prerelease. Otherwise, set the patch to 0, and increment the | ||
| * minor. So `1.2.0-beta` becomes `1.2.0`, and `1.2.3` becomes `1.3.0`. | ||
| * | ||
| * - `'patch'` If the version has a prerelease, then simply drop the | ||
| * prerelease. Otherwise, increment the patch value. So `1.2.3-beta` becomes | ||
| * `1.2.3` and `1.2.3` becomes `1.2.4`. | ||
| * | ||
| * - `'premajor'` Set the patch and minor versions to `0`, increment the major | ||
| * version, and add a prerelease, using the optional identifier. | ||
| * | ||
| * - `'preminor'` Set the patch version to `0`, increment the minor version, | ||
| * and add a prerelease, using the optional identifier. | ||
| * | ||
| * - `'prepatch'` If a prerelease is already present, increment the patch | ||
| * version, otherwise leave it untouched, and add a prerelease, using the | ||
| * optional identifier. | ||
| * | ||
| * - `'prerelease'` If a prerelease version is present, then behave the same as | ||
| * `'prepatch'`. Otherwise, add a prerelease, using the optional identifier. | ||
| * | ||
| * - `'pre'` This is mostly for use by the other prerelease incrementers. | ||
| * | ||
| * - If a prerelease identifier is provided: | ||
| * | ||
| * Update that named portion of the prerelease. For example, | ||
| * `inc('1.2.3-beta.4', 'pre', 'beta')` would result in `1.2.3-beta.5`. | ||
| * | ||
| * If there is no prerelease identifier by that name, then replace the | ||
| * prerelease with `[name]`. So `inc('1.2.3-alpha.4', 'pre', 'beta')` | ||
| * would result in `1.2.3-beta`. | ||
| * | ||
| * If the prerelease identifer is present, but has no numeric value | ||
| * following it, then add `0`. So `inc('1.2.3-beta', 'pre', 'beta')` | ||
| * would result in `1.2.3-beta.0`. | ||
| * | ||
| * - If no prerelease identifier is provided: | ||
| * | ||
| * If there is no current prerelease, then set the prerelease to `0`. So, | ||
| * `inc('1.2.3', 'pre')` becomes `1.2.3-0`. | ||
| * | ||
| * If the last item in the prerelease is numeric, then increment it. So, | ||
| * `inc('1.2.3-beta.3', 'pre')` becomes `1.2.3-beta.4`. | ||
| */ | ||
| inc(part, prereleaseIdentifier) { | ||
| switch (part) { | ||
| case 'premajor': | ||
| this.prerelease = undefined; | ||
| this.patch = 0; | ||
| this.minor = 0; | ||
| this.major++; | ||
| this.inc('pre', prereleaseIdentifier); | ||
| break; | ||
| case 'preminor': | ||
| this.prerelease = undefined; | ||
| this.patch = 0; | ||
| this.minor++; | ||
| this.inc('pre', prereleaseIdentifier); | ||
| break; | ||
| case 'prepatch': | ||
| this.prerelease = undefined; | ||
| this.inc('patch'); | ||
| this.inc('pre', prereleaseIdentifier); | ||
| break; | ||
| case 'prerelease': | ||
| if (!this.prerelease?.length) | ||
| this.inc('patch', prereleaseIdentifier); | ||
| this.inc('pre', prereleaseIdentifier); | ||
| break; | ||
| case 'pre': { | ||
| // this is a bit different than node-semver's logic, but simpler | ||
| // always do zero-based incrementing, and either bump the existing | ||
| // numeric pr value, or add a `.0` after the identifier. | ||
| if (!prereleaseIdentifier) { | ||
| if (!this.prerelease?.length) { | ||
| this.prerelease = [0]; | ||
| break; | ||
| } | ||
| const last = this.prerelease[this.prerelease.length - 1]; | ||
| if (typeof last === 'number') { | ||
| this.prerelease[this.prerelease.length - 1] = last + 1; | ||
| } | ||
| else { | ||
| this.prerelease.push(0); | ||
| } | ||
| break; | ||
| } | ||
| if (!this.prerelease?.length) { | ||
| this.prerelease = [prereleaseIdentifier]; | ||
| break; | ||
| } | ||
| const i = this.prerelease.indexOf(maybeNumber(prereleaseIdentifier)); | ||
| if (i === -1) { | ||
| this.prerelease = [prereleaseIdentifier]; | ||
| break; | ||
| } | ||
| const baseValue = this.prerelease[i + 1]; | ||
| if (typeof baseValue === 'number') { | ||
| this.prerelease[i + 1] = baseValue + 1; | ||
| break; | ||
| } | ||
| if (i === this.prerelease.length - 1) { | ||
| this.prerelease.push(0); | ||
| break; | ||
| } | ||
| this.prerelease.splice(i + 1, 0, 0); | ||
| break; | ||
| } | ||
| case 'major': | ||
| if (!this.prerelease?.length || this.minor || this.patch) | ||
| this.major++; | ||
| this.prerelease = undefined; | ||
| this.patch = 0; | ||
| this.minor = 0; | ||
| break; | ||
| case 'minor': | ||
| if (!this.prerelease?.length || this.patch) | ||
| this.minor++; | ||
| this.prerelease = undefined; | ||
| this.patch = 0; | ||
| break; | ||
| case 'patch': | ||
| if (!this.prerelease?.length) | ||
| this.patch++; | ||
| this.prerelease = undefined; | ||
| break; | ||
| default: | ||
| throw typeError('Invalid increment identifier', { | ||
| version: this, | ||
| found: part, | ||
| validOptions: [ | ||
| 'major', | ||
| 'minor', | ||
| 'patch', | ||
| 'premajor', | ||
| 'preminor', | ||
| 'prepatch', | ||
| 'prerelease', | ||
| 'pre', | ||
| ], | ||
| }, this.inc); | ||
| } | ||
| this.raw = this.toString(); | ||
| return this; | ||
| } | ||
| } |
+4
-4
| { | ||
| "name": "@vltpkg/semver", | ||
| "description": "The semantic version parser used by vlt", | ||
| "version": "1.0.0-rc.23", | ||
| "version": "1.0.0-rc.24", | ||
| "repository": { | ||
@@ -15,5 +15,5 @@ "type": "git", | ||
| "dependencies": { | ||
| "@vltpkg/error-cause": "1.0.0-rc.23", | ||
| "@vltpkg/fast-split": "1.0.0-rc.23", | ||
| "@vltpkg/types": "1.0.0-rc.23" | ||
| "@vltpkg/error-cause": "1.0.0-rc.24", | ||
| "@vltpkg/fast-split": "1.0.0-rc.24", | ||
| "@vltpkg/types": "1.0.0-rc.24" | ||
| }, | ||
@@ -20,0 +20,0 @@ "devDependencies": { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 3 instances in 1 package
78450
670.02%11
266.67%1880
Infinity%1
-75%1
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
Updated