semver-effect
Advanced tools
| import { Data } from "effect"; | ||
| //#region src/errors/EmptyCacheError.ts | ||
| /** | ||
| * Tagged error base for {@link EmptyCacheError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `EmptyCacheError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use {@link EmptyCacheError} | ||
| * directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const EmptyCacheErrorBase = Data.TaggedError("EmptyCacheError"); | ||
| /** | ||
| * Indicates that a {@link VersionCache} operation was attempted on an empty cache. | ||
| * | ||
| * Returned by query and grouping operations (e.g., `versions`, `latest`, `oldest`, | ||
| * `groupBy`) when no versions have been loaded into the cache. | ||
| * | ||
| * @see {@link VersionCache} | ||
| * @public | ||
| */ | ||
| var EmptyCacheError = class extends EmptyCacheErrorBase { | ||
| get message() { | ||
| return "Version cache is empty"; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { EmptyCacheError, EmptyCacheErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/InvalidBumpError.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidBumpError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `InvalidBumpError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use {@link InvalidBumpError} | ||
| * directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const InvalidBumpErrorBase = Data.TaggedError("InvalidBumpError"); | ||
| /** | ||
| * Indicates that a version bump operation cannot be applied to the given version. | ||
| * | ||
| * @see {@link bumpMajor} | ||
| * @see {@link bumpMinor} | ||
| * @see {@link bumpPatch} | ||
| * @see {@link bumpPrerelease} | ||
| * @see {@link bumpRelease} | ||
| * @public | ||
| */ | ||
| var InvalidBumpError = class extends InvalidBumpErrorBase { | ||
| get message() { | ||
| return `Cannot apply ${this.type} bump to version ${this.version.toString()}`; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { InvalidBumpError, InvalidBumpErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/InvalidComparatorError.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidComparatorError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `InvalidComparatorError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link InvalidComparatorError} directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const InvalidComparatorErrorBase = Data.TaggedError("InvalidComparatorError"); | ||
| /** | ||
| * Indicates that a string could not be parsed as a valid single {@link Comparator}. | ||
| * | ||
| * Returned by {@link parseSingleComparator} (and `SemVerParser.parseComparator`) when the | ||
| * input is not a valid `[operator]major.minor.patch[-prerelease][+build]` string. | ||
| * Wildcards and range syntax are not allowed in single comparator parsing. | ||
| * | ||
| * @see {@link Comparator} | ||
| * @public | ||
| */ | ||
| var InvalidComparatorError = class extends InvalidComparatorErrorBase { | ||
| get message() { | ||
| const base = `Invalid comparator: "${this.input}"`; | ||
| return this.position !== void 0 ? `${base} at position ${this.position}` : base; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { InvalidComparatorError, InvalidComparatorErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/InvalidPrereleaseError.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidPrereleaseError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `InvalidPrereleaseError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link InvalidPrereleaseError} directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const InvalidPrereleaseErrorBase = Data.TaggedError("InvalidPrereleaseError"); | ||
| /** | ||
| * Indicates that a prerelease identifier is not valid per SemVer 2.0.0. | ||
| * | ||
| * Prerelease identifiers must be non-empty and composed of alphanumerics and | ||
| * hyphens. Numeric identifiers must not have leading zeros. | ||
| * | ||
| * @see {@link SemVer} | ||
| * @see {@link https://semver.org/#spec-item-9 | SemVer 2.0.0 Section 9} | ||
| * @public | ||
| */ | ||
| var InvalidPrereleaseError = class extends InvalidPrereleaseErrorBase { | ||
| get message() { | ||
| return `Invalid prerelease identifier: "${this.input}"`; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { InvalidPrereleaseError, InvalidPrereleaseErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/InvalidRangeError.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidRangeError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `InvalidRangeError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use {@link InvalidRangeError} | ||
| * directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const InvalidRangeErrorBase = Data.TaggedError("InvalidRangeError"); | ||
| /** | ||
| * Indicates that a string could not be parsed as a valid SemVer range expression. | ||
| * | ||
| * Returned by {@link parseRange} and the `SemVerParser.parseRange` service method | ||
| * when the input is not a valid range. Supported syntax includes hyphen ranges, | ||
| * X-ranges, tilde ranges, caret ranges, and `||`-separated unions, but all | ||
| * version components must be strictly valid SemVer 2.0.0. | ||
| * | ||
| * @see {@link Range} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| var InvalidRangeError = class extends InvalidRangeErrorBase { | ||
| get message() { | ||
| const base = `Invalid range expression: "${this.input}"`; | ||
| return this.position !== void 0 ? `${base} at position ${this.position}` : base; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { InvalidRangeError, InvalidRangeErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/InvalidVersionError.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidVersionError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `InvalidVersionError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link InvalidVersionError} directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const InvalidVersionErrorBase = Data.TaggedError("InvalidVersionError"); | ||
| /** | ||
| * Indicates that a string could not be parsed as a valid SemVer 2.0.0 version. | ||
| * | ||
| * This error is returned when {@link parseValidSemVer} (or the `SemVerParser` service) | ||
| * encounters input that does not conform to the `major.minor.patch[-prerelease][+build]` | ||
| * format. Unlike node-semver, no loose parsing or `v`-prefix coercion is performed. | ||
| * | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| var InvalidVersionError = class extends InvalidVersionErrorBase { | ||
| get message() { | ||
| const base = `Invalid version string: "${this.input}"`; | ||
| return this.position !== void 0 ? `${base} at position ${this.position}` : base; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { InvalidVersionError, InvalidVersionErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/UnsatisfiableConstraintError.ts | ||
| /** | ||
| * Tagged error base for {@link UnsatisfiableConstraintError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `UnsatisfiableConstraintError` | ||
| * extends it directly: API Extractor requires a class's release tag to be at least | ||
| * as public as every type in its `extends` clause. Consumers should use | ||
| * {@link UnsatisfiableConstraintError} directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const UnsatisfiableConstraintErrorBase = Data.TaggedError("UnsatisfiableConstraintError"); | ||
| /** | ||
| * Indicates that the intersection of two or more {@link Range}s produces an | ||
| * empty (unsatisfiable) result. | ||
| * | ||
| * Returned by {@link intersect} when no comparator set in the cross-product | ||
| * of the input ranges is satisfiable. | ||
| * | ||
| * @see {@link intersect} | ||
| * @see {@link Range} | ||
| * @public | ||
| */ | ||
| var UnsatisfiableConstraintError = class extends UnsatisfiableConstraintErrorBase { | ||
| get message() { | ||
| const count = this.constraints.length; | ||
| return `No version satisfies all ${count} constraint${count === 1 ? "" : "s"}`; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { UnsatisfiableConstraintError, UnsatisfiableConstraintErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/UnsatisfiedRangeError.ts | ||
| /** | ||
| * Tagged error base for {@link UnsatisfiedRangeError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `UnsatisfiedRangeError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link UnsatisfiedRangeError} directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const UnsatisfiedRangeErrorBase = Data.TaggedError("UnsatisfiedRangeError"); | ||
| /** | ||
| * Indicates that no version in a given set satisfies a {@link Range}. | ||
| * | ||
| * Returned by `VersionCache.resolve` and `VersionCache.resolveString` | ||
| * when the cache contains versions but none match the requested range. | ||
| * | ||
| * @see {@link VersionCache} | ||
| * @see {@link Range} | ||
| * @public | ||
| */ | ||
| var UnsatisfiedRangeError = class extends UnsatisfiedRangeErrorBase { | ||
| get message() { | ||
| const count = this.available.length; | ||
| return `No version satisfies range ${this.range.toString()} (${count} version${count === 1 ? "" : "s"} available)`; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { UnsatisfiedRangeError, UnsatisfiedRangeErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/VersionFetchError.ts | ||
| /** | ||
| * Tagged error base for {@link VersionFetchError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `VersionFetchError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link VersionFetchError} directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const VersionFetchErrorBase = Data.TaggedError("VersionFetchError"); | ||
| /** | ||
| * Indicates that fetching versions from an external source failed. | ||
| * | ||
| * Returned by {@link VersionFetcher} implementations when a network request or | ||
| * registry lookup fails. The `source` field identifies the registry or endpoint, | ||
| * and `cause` may contain the underlying error. | ||
| * | ||
| * @see {@link VersionFetcher} | ||
| * @public | ||
| */ | ||
| var VersionFetchError = class extends VersionFetchErrorBase {}; | ||
| //#endregion | ||
| export { VersionFetchError, VersionFetchErrorBase }; |
| import { Data } from "effect"; | ||
| //#region src/errors/VersionNotFoundError.ts | ||
| /** | ||
| * Tagged error base for {@link VersionNotFoundError}. | ||
| * | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `VersionNotFoundError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link VersionNotFoundError} directly rather than referencing this base. | ||
| * | ||
| * @public | ||
| */ | ||
| const VersionNotFoundErrorBase = Data.TaggedError("VersionNotFoundError"); | ||
| /** | ||
| * Indicates that a specific version was not found in the {@link VersionCache}. | ||
| * | ||
| * Returned by navigation operations (`diff`, `next`, `prev`) when the referenced | ||
| * version has not been loaded into the cache. | ||
| * | ||
| * @see {@link VersionCache} | ||
| * @public | ||
| */ | ||
| var VersionNotFoundError = class extends VersionNotFoundErrorBase { | ||
| get message() { | ||
| return `Version not found in cache: ${this.version.toString()}`; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { VersionNotFoundError, VersionNotFoundErrorBase }; |
| import { SemVerParser } from "../services/SemVerParser.js"; | ||
| import { parseRangeSet, parseSingleComparator, parseValidSemVer } from "../utils/grammar.js"; | ||
| import { normalizeRange } from "../utils/normalize.js"; | ||
| import { Effect, Layer } from "effect"; | ||
| //#region src/layers/SemVerParserLive.ts | ||
| /** | ||
| * Live Effect `Layer` that provides the {@link SemVerParser} service. | ||
| * | ||
| * This layer has no dependencies and can be provided directly. It delegates to | ||
| * the strict SemVer 2.0.0 recursive-descent parser implemented in the grammar | ||
| * module, with range normalization applied to parsed range expressions. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVerParser, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const parser = yield* SemVerParser; | ||
| * const v = yield* parser.parseVersion("1.0.0"); | ||
| * }).pipe(Effect.provide(SemVerParserLive)); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVerParser} | ||
| * @see {@link https://effect.website/docs/context-management/layers | Effect Layers} | ||
| * @public | ||
| */ | ||
| const SemVerParserLive = Layer.succeed(SemVerParser, SemVerParser.of({ | ||
| parseVersion: parseValidSemVer, | ||
| parseRange: (input) => Effect.map(parseRangeSet(input), normalizeRange), | ||
| parseComparator: parseSingleComparator | ||
| })); | ||
| //#endregion | ||
| export { SemVerParserLive }; |
| import { EmptyCacheError } from "../errors/EmptyCacheError.js"; | ||
| import { UnsatisfiedRangeError } from "../errors/UnsatisfiedRangeError.js"; | ||
| import { VersionNotFoundError } from "../errors/VersionNotFoundError.js"; | ||
| import { SemVerParser } from "../services/SemVerParser.js"; | ||
| import { SemVerOrder } from "../utils/order.js"; | ||
| import { VersionCache } from "../services/VersionCache.js"; | ||
| import { diff } from "../utils/diff.js"; | ||
| import { satisfies } from "../utils/matching.js"; | ||
| import { Effect, Layer, Option, Ref, SortedSet } from "effect"; | ||
| //#region src/layers/VersionCacheLive.ts | ||
| const toArray = (set) => Array.from(SortedSet.values(set)); | ||
| const binarySearch = (arr, target) => { | ||
| let lo = 0; | ||
| let hi = arr.length - 1; | ||
| while (lo <= hi) { | ||
| const mid = lo + hi >>> 1; | ||
| const cmp = SemVerOrder(arr[mid], target); | ||
| if (cmp === 0) return mid; | ||
| if (cmp < 0) lo = mid + 1; | ||
| else hi = mid - 1; | ||
| } | ||
| return -1; | ||
| }; | ||
| const requireNonEmptySet = (set) => { | ||
| if (SortedSet.size(set) === 0) return Effect.fail(new EmptyCacheError()); | ||
| return Effect.succeed(toArray(set)); | ||
| }; | ||
| /** | ||
| * Live Effect `Layer` that provides the {@link VersionCache} service. | ||
| * | ||
| * Backed by an Effect `Ref` containing a `SortedSet` ordered by {@link SemVerOrder}. | ||
| * Requires a {@link SemVerParser} in its dependency graph (used by `resolveString` | ||
| * to parse range strings). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer, VersionCache, VersionCacheLive, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * | ||
| * const AppLayer = Layer.merge(VersionCacheLive, SemVerParserLive); | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const cache = yield* VersionCache; | ||
| * const v = new SemVer({ major: 1, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * yield* cache.add(v); | ||
| * const latest = yield* cache.latest(); | ||
| * }).pipe(Effect.provide(AppLayer)); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionCache} | ||
| * @see {@link SemVerParserLive} | ||
| * @see {@link https://effect.website/docs/context-management/layers | Effect Layers} | ||
| * @public | ||
| */ | ||
| const VersionCacheLive = Layer.effect(VersionCache, Effect.gen(function* () { | ||
| const parser = yield* SemVerParser; | ||
| const ref = yield* Ref.make(SortedSet.empty(SemVerOrder)); | ||
| const resolveFromRef = (range) => Effect.flatMap(Ref.get(ref), (set) => { | ||
| const arr = toArray(set); | ||
| for (let i = arr.length - 1; i >= 0; i--) if (satisfies(arr[i], range)) return Effect.succeed(arr[i]); | ||
| return Effect.fail(new UnsatisfiedRangeError({ | ||
| range, | ||
| available: arr | ||
| })); | ||
| }); | ||
| return VersionCache.of({ | ||
| load: (versions) => Ref.set(ref, SortedSet.fromIterable(versions, SemVerOrder)), | ||
| add: (version) => Ref.update(ref, SortedSet.add(version)), | ||
| remove: (version) => Ref.update(ref, SortedSet.remove(version)), | ||
| get versions() { | ||
| return Effect.flatMap(Ref.get(ref), requireNonEmptySet); | ||
| }, | ||
| latest: () => Effect.flatMap(Ref.get(ref), (set) => Effect.map(requireNonEmptySet(set), (arr) => arr[arr.length - 1])), | ||
| oldest: () => Effect.flatMap(Ref.get(ref), (set) => Effect.map(requireNonEmptySet(set), (arr) => arr[0])), | ||
| resolve: (range) => resolveFromRef(range), | ||
| resolveString: (input) => Effect.flatMap(parser.parseRange(input), resolveFromRef), | ||
| filter: (range) => Effect.flatMap(Ref.get(ref), (set) => Effect.map(requireNonEmptySet(set), (arr) => arr.filter((v) => satisfies(v, range)))), | ||
| groupBy: (strategy) => Effect.flatMap(Ref.get(ref), (set) => Effect.map(requireNonEmptySet(set), (arr) => { | ||
| const map = /* @__PURE__ */ new Map(); | ||
| for (const ver of arr) { | ||
| let key; | ||
| switch (strategy) { | ||
| case "major": | ||
| key = `${ver.major}`; | ||
| break; | ||
| case "minor": | ||
| key = `${ver.major}.${ver.minor}`; | ||
| break; | ||
| case "patch": | ||
| key = `${ver.major}.${ver.minor}.${ver.patch}`; | ||
| break; | ||
| } | ||
| const existing = map.get(key); | ||
| map.set(key, existing ? [...existing, ver] : [ver]); | ||
| } | ||
| return map; | ||
| })), | ||
| latestByMajor: () => Effect.flatMap(Ref.get(ref), (set) => Effect.map(requireNonEmptySet(set), (arr) => { | ||
| const map = /* @__PURE__ */ new Map(); | ||
| for (const ver of arr) map.set(ver.major, ver); | ||
| return Array.from(map.values()); | ||
| })), | ||
| latestByMinor: () => Effect.flatMap(Ref.get(ref), (set) => Effect.map(requireNonEmptySet(set), (arr) => { | ||
| const map = /* @__PURE__ */ new Map(); | ||
| for (const ver of arr) map.set(`${ver.major}.${ver.minor}`, ver); | ||
| return Array.from(map.values()); | ||
| })), | ||
| diff: (a, b) => Effect.flatMap(Ref.get(ref), (set) => { | ||
| if (!SortedSet.has(set, a)) return Effect.fail(new VersionNotFoundError({ version: a })); | ||
| if (!SortedSet.has(set, b)) return Effect.fail(new VersionNotFoundError({ version: b })); | ||
| return Effect.succeed(diff(a, b)); | ||
| }), | ||
| next: (version) => Effect.flatMap(Ref.get(ref), (set) => { | ||
| if (!SortedSet.has(set, version)) return Effect.fail(new VersionNotFoundError({ version })); | ||
| const arr = toArray(set); | ||
| const idx = binarySearch(arr, version); | ||
| if (idx < arr.length - 1) return Effect.succeed(Option.some(arr[idx + 1])); | ||
| return Effect.succeed(Option.none()); | ||
| }), | ||
| prev: (version) => Effect.flatMap(Ref.get(ref), (set) => { | ||
| if (!SortedSet.has(set, version)) return Effect.fail(new VersionNotFoundError({ version })); | ||
| const arr = toArray(set); | ||
| const idx = binarySearch(arr, version); | ||
| if (idx > 0) return Effect.succeed(Option.some(arr[idx - 1])); | ||
| return Effect.succeed(Option.none()); | ||
| }) | ||
| }); | ||
| })); | ||
| //#endregion | ||
| export { VersionCacheLive }; |
| import { SemVer } from "./SemVer.js"; | ||
| import { Schema } from "effect"; | ||
| //#region src/schemas/Comparator.ts | ||
| /** | ||
| * A single version constraint consisting of a comparison operator and a version. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { Comparator } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const comp = yield* Comparator.parse(">=1.2.3"); | ||
| * console.log(comp.operator); // ">=" | ||
| * console.log(comp.version.toString()); // "1.2.3" | ||
| * console.log(comp.test(new SemVer({ major: 2, minor: 0, patch: 0, prerelease: [], build: [] }))); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Range} | ||
| * @see {@link SemVer} | ||
| * @public | ||
| */ | ||
| var Comparator = class extends Schema.TaggedClass()("Comparator", { | ||
| operator: Schema.Literal("=", ">", ">=", "<", "<="), | ||
| version: SemVer | ||
| }) { | ||
| /** Parse a comparator string (e.g. `">=1.2.3"`). Wired at module load by index.ts. */ | ||
| static parse; | ||
| /** Test whether a version satisfies this comparator. */ | ||
| test(version) { | ||
| const cmp = version.compare(this.version); | ||
| switch (this.operator) { | ||
| case "=": return cmp === 0; | ||
| case ">": return cmp > 0; | ||
| case ">=": return cmp >= 0; | ||
| case "<": return cmp < 0; | ||
| case "<=": return cmp <= 0; | ||
| } | ||
| } | ||
| toString() { | ||
| return `${this.operator === "=" ? "" : this.operator}${this.version.toString()}`; | ||
| } | ||
| [Symbol.for("nodejs.util.inspect.custom")]() { | ||
| return this.toString(); | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { Comparator }; |
| import { Comparator } from "./Comparator.js"; | ||
| import { Schema } from "effect"; | ||
| //#region src/schemas/Range.ts | ||
| /** | ||
| * A SemVer range expression, represented as a union (OR) of {@link ComparatorSet}s. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { Range, SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const range = yield* Range.parse("^1.0.0"); | ||
| * const v = new SemVer({ major: 1, minor: 5, patch: 0, prerelease: [], build: [] }); | ||
| * console.log(range.test(v)); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Comparator} | ||
| * @see {@link ComparatorSet} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| var Range = class extends Schema.TaggedClass()("Range", { sets: Schema.Array(Schema.Array(Comparator)) }) { | ||
| /** Parse a SemVer range expression. Wired at module load by index.ts. */ | ||
| static parse; | ||
| /** Test whether a version satisfies a range. Dual API. Wired at module load. */ | ||
| static satisfies; | ||
| /** Filter versions that satisfy a range. Dual API. Wired at module load. */ | ||
| static filter; | ||
| /** Highest satisfying version. Dual API. Wired at module load. */ | ||
| static maxSatisfying; | ||
| /** Lowest satisfying version. Dual API. Wired at module load. */ | ||
| static minSatisfying; | ||
| /** Test whether a version satisfies this range. */ | ||
| test(version) { | ||
| return this.sets.some((set) => satisfiesSet(version, set)); | ||
| } | ||
| /** Filter versions that satisfy this range, preserving order. */ | ||
| filter(versions) { | ||
| return versions.filter((v) => this.test(v)); | ||
| } | ||
| toString() { | ||
| return this.sets.map((set) => set.map((c) => c.toString()).join(" ")).join(" || "); | ||
| } | ||
| [Symbol.for("nodejs.util.inspect.custom")]() { | ||
| return this.toString(); | ||
| } | ||
| }; | ||
| const satisfiesSet = (version, set) => { | ||
| if (set.length === 0) return true; | ||
| if (version.prerelease.length > 0) { | ||
| if (!set.some((c) => c.version.prerelease.length > 0 && c.version.major === version.major && c.version.minor === version.minor && c.version.patch === version.patch)) return false; | ||
| } | ||
| return set.every((c) => c.test(version)); | ||
| }; | ||
| //#endregion | ||
| export { Range }; |
| import { Equal, Hash, Schema } from "effect"; | ||
| //#region src/schemas/SemVer.ts | ||
| const comparePre = (a, b) => { | ||
| if (typeof a === "number" && typeof b === "number") return a - b; | ||
| if (typeof a === "string" && typeof b === "string") return a < b ? -1 : a > b ? 1 : 0; | ||
| if (typeof a === "number") return -1; | ||
| return 1; | ||
| }; | ||
| /** | ||
| * A parsed SemVer 2.0.0 version, represented as an Effect `Schema.TaggedClass`. | ||
| * | ||
| * Provides instance methods for comparison, bumping, and predicates, plus | ||
| * static methods for parsing and collection operations. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.2.3"); | ||
| * const next = v.bump.minor(); // 1.3.0 | ||
| * console.log(v.gt(next)); // false | ||
| * console.log(next.isStable); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| var SemVer = class SemVer extends Schema.TaggedClass()("SemVer", { | ||
| major: Schema.Number, | ||
| minor: Schema.Number, | ||
| patch: Schema.Number, | ||
| prerelease: Schema.Array(Schema.Union(Schema.String, Schema.Number)), | ||
| build: Schema.Array(Schema.String) | ||
| }) { | ||
| /** Parse a strict SemVer 2.0.0 string. @remarks Import from `"semver-effect"`, not the schema subpath. */ | ||
| static parse; | ||
| /** Convenience positional constructor. Wired at module load by index.ts. */ | ||
| static of; | ||
| /** Compare two versions. Returns `-1`, `0`, or `1`. Dual API. */ | ||
| static compare; | ||
| /** Test whether `self > that`. Dual API. */ | ||
| static gt; | ||
| /** Test whether `self >= that`. Dual API. */ | ||
| static gte; | ||
| /** Test whether `self < that`. Dual API. */ | ||
| static lt; | ||
| /** Test whether `self <= that`. Dual API. */ | ||
| static lte; | ||
| /** Test whether `self !== that`. Dual API. */ | ||
| static neq; | ||
| /** Test whether two versions are equal (ignores build). Dual API. */ | ||
| static equal; | ||
| /** Compute diff between two versions. Dual API. */ | ||
| static diff; | ||
| /** Sort versions ascending. */ | ||
| static sort; | ||
| /** Sort versions descending. */ | ||
| static rsort; | ||
| /** Highest version, or `Option.none()` if empty. */ | ||
| static max; | ||
| /** Lowest version, or `Option.none()` if empty. */ | ||
| static min; | ||
| /** Compare `this` to `that` per SemVer 2.0.0 precedence. Returns `-1`, `0`, or `1`. */ | ||
| compare(that) { | ||
| if (this.major !== that.major) return this.major > that.major ? 1 : -1; | ||
| if (this.minor !== that.minor) return this.minor > that.minor ? 1 : -1; | ||
| if (this.patch !== that.patch) return this.patch > that.patch ? 1 : -1; | ||
| const aPre = this.prerelease; | ||
| const bPre = that.prerelease; | ||
| if (aPre.length === 0 && bPre.length === 0) return 0; | ||
| if (aPre.length === 0) return 1; | ||
| if (bPre.length === 0) return -1; | ||
| const len = Math.min(aPre.length, bPre.length); | ||
| for (let i = 0; i < len; i++) { | ||
| const cmp = comparePre(aPre[i], bPre[i]); | ||
| if (cmp !== 0) return cmp < 0 ? -1 : 1; | ||
| } | ||
| if (aPre.length !== bPre.length) return aPre.length > bPre.length ? 1 : -1; | ||
| return 0; | ||
| } | ||
| /** Test whether `this > that`. */ | ||
| gt(that) { | ||
| return this.compare(that) === 1; | ||
| } | ||
| /** Test whether `this >= that`. */ | ||
| gte(that) { | ||
| return this.compare(that) >= 0; | ||
| } | ||
| /** Test whether `this < that`. */ | ||
| lt(that) { | ||
| return this.compare(that) === -1; | ||
| } | ||
| /** Test whether `this <= that`. */ | ||
| lte(that) { | ||
| return this.compare(that) <= 0; | ||
| } | ||
| /** Test whether `this` equals `that` (ignores build metadata). */ | ||
| equal(that) { | ||
| return this.compare(that) === 0; | ||
| } | ||
| /** Alias for {@link equal}. */ | ||
| eq(that) { | ||
| return this.equal(that); | ||
| } | ||
| /** Test whether `this` does not equal `that` (ignores build metadata). */ | ||
| neq(that) { | ||
| return this.compare(that) !== 0; | ||
| } | ||
| /** Whether this is a prerelease version. */ | ||
| get isPrerelease() { | ||
| return this.prerelease.length > 0; | ||
| } | ||
| /** Whether this is a stable (non-prerelease) version. */ | ||
| get isStable() { | ||
| return this.prerelease.length === 0; | ||
| } | ||
| _bump; | ||
| /** Version bumping operations. */ | ||
| get bump() { | ||
| if (!this._bump) this._bump = new SemVerBump(this); | ||
| return this._bump; | ||
| } | ||
| [Equal.symbol](that) { | ||
| if (!(that instanceof SemVer)) return false; | ||
| return this.major === that.major && this.minor === that.minor && this.patch === that.patch && this.prerelease.length === that.prerelease.length && this.prerelease.every((v, i) => v === that.prerelease[i]); | ||
| } | ||
| [Hash.symbol]() { | ||
| let h = Hash.hash(this.major); | ||
| h = Hash.combine(h)(Hash.hash(this.minor)); | ||
| h = Hash.combine(h)(Hash.hash(this.patch)); | ||
| for (const item of this.prerelease) h = Hash.combine(h)(Hash.hash(item)); | ||
| return Hash.cached(this)(h); | ||
| } | ||
| toString() { | ||
| let s = `${this.major}.${this.minor}.${this.patch}`; | ||
| if (this.prerelease.length > 0) s += `-${this.prerelease.join(".")}`; | ||
| if (this.build.length > 0) s += `+${this.build.join(".")}`; | ||
| return s; | ||
| } | ||
| toJSON() { | ||
| return { | ||
| _tag: "SemVer", | ||
| major: this.major, | ||
| minor: this.minor, | ||
| patch: this.patch, | ||
| prerelease: this.prerelease.slice(), | ||
| build: this.build.slice() | ||
| }; | ||
| } | ||
| [Symbol.for("nodejs.util.inspect.custom")]() { | ||
| return this.toString(); | ||
| } | ||
| }; | ||
| /** | ||
| * Grouped bump operations returned by {@link SemVer.bump}. | ||
| * | ||
| * @public | ||
| */ | ||
| var SemVerBump = class { | ||
| v; | ||
| constructor(v) { | ||
| this.v = v; | ||
| } | ||
| /** Bump major (resets minor, patch, prerelease, build). */ | ||
| major() { | ||
| return new SemVer({ | ||
| major: this.v.major + 1, | ||
| minor: 0, | ||
| patch: 0, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| /** Bump minor (resets patch, prerelease, build). */ | ||
| minor() { | ||
| return new SemVer({ | ||
| major: this.v.major, | ||
| minor: this.v.minor + 1, | ||
| patch: 0, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| /** Bump patch (resets prerelease, build). */ | ||
| patch() { | ||
| return new SemVer({ | ||
| major: this.v.major, | ||
| minor: this.v.minor, | ||
| patch: this.v.patch + 1, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| /** Bump prerelease, optionally with a named identifier. */ | ||
| prerelease(id) { | ||
| const { major, minor, patch } = this.v; | ||
| const pre = this.v.prerelease; | ||
| if (pre.length === 0) return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch: patch + 1, | ||
| prerelease: id !== void 0 ? [id, 0] : [0], | ||
| build: [] | ||
| }); | ||
| if (id !== void 0) { | ||
| if ((typeof pre[0] === "string" ? pre[0] : null) !== id) return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: [id, 0], | ||
| build: [] | ||
| }); | ||
| } | ||
| const last = pre[pre.length - 1]; | ||
| if (typeof last === "number") { | ||
| const newPre = [...pre]; | ||
| newPre[newPre.length - 1] = last + 1; | ||
| return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: newPre, | ||
| build: [] | ||
| }); | ||
| } | ||
| return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: [...pre, 0], | ||
| build: [] | ||
| }); | ||
| } | ||
| /** Strip prerelease and build, keeping major.minor.patch. */ | ||
| release() { | ||
| return new SemVer({ | ||
| major: this.v.major, | ||
| minor: this.v.minor, | ||
| patch: this.v.patch, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { SemVer, SemVerBump }; |
| import { SemVer } from "./SemVer.js"; | ||
| import { Schema } from "effect"; | ||
| //#region src/schemas/VersionDiff.ts | ||
| /** | ||
| * The result of computing the difference between two {@link SemVer} versions. | ||
| * | ||
| * Contains the classification of the change (`type`) along with the signed | ||
| * numeric deltas for `major`, `minor`, and `patch` fields. The `from` and `to` | ||
| * fields preserve the original version objects. | ||
| * | ||
| * The `type` field indicates the highest-precedence field that differs: | ||
| * - `"major"` — major versions differ | ||
| * - `"minor"` — major is equal, minor versions differ | ||
| * - `"patch"` — major and minor are equal, patch versions differ | ||
| * - `"prerelease"` — only prerelease identifiers differ | ||
| * - `"build"` — only build metadata differs | ||
| * - `"none"` — versions are identical | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseValidSemVer, diff } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* parseValidSemVer("1.2.3"); | ||
| * const b = yield* parseValidSemVer("2.0.0"); | ||
| * const d = diff(a, b); | ||
| * console.log(d.type); // "major" | ||
| * console.log(d.major); // 1 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link diff} | ||
| * @see {@link SemVer} | ||
| * @public | ||
| */ | ||
| var VersionDiff = class extends Schema.TaggedClass()("VersionDiff", { | ||
| type: Schema.Literal("major", "minor", "patch", "prerelease", "build", "none"), | ||
| from: SemVer, | ||
| to: SemVer, | ||
| major: Schema.Number, | ||
| minor: Schema.Number, | ||
| patch: Schema.Number | ||
| }) { | ||
| toString() { | ||
| return `${this.type} (${this.from.toString()} → ${this.to.toString()})`; | ||
| } | ||
| toJSON() { | ||
| return { | ||
| _tag: "VersionDiff", | ||
| type: this.type, | ||
| from: this.from.toJSON(), | ||
| to: this.to.toJSON(), | ||
| major: this.major, | ||
| minor: this.minor, | ||
| patch: this.patch | ||
| }; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { VersionDiff }; |
| import { Context } from "effect"; | ||
| //#region src/services/SemVerParser.ts | ||
| /** | ||
| * Effect service interface for parsing SemVer 2.0.0 strings. | ||
| * | ||
| * Provides effectful parsing of version strings, range expressions, and | ||
| * individual comparators. All parsing is strict SemVer 2.0.0; no loose mode | ||
| * or `v`-prefix coercion is supported. | ||
| * | ||
| * Use {@link SemVerParserLive} to provide a concrete implementation. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVerParser, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const parser = yield* SemVerParser; | ||
| * const version = yield* parser.parseVersion("1.2.3"); | ||
| * const range = yield* parser.parseRange("^1.0.0"); | ||
| * const comp = yield* parser.parseComparator(">=2.0.0"); | ||
| * }).pipe(Effect.provide(SemVerParserLive)); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVerParserLive} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @public | ||
| */ | ||
| var SemVerParser = class extends Context.Tag("semver-effect/SemVerParser")() {}; | ||
| //#endregion | ||
| export { SemVerParser }; |
| import { Context } from "effect"; | ||
| //#region src/services/VersionCache.ts | ||
| /** | ||
| * Effect service interface for an in-memory sorted version cache. | ||
| * | ||
| * Provides mutation, query, resolution, grouping, and navigation operations | ||
| * over a set of {@link SemVer} versions. Versions are maintained in sorted | ||
| * order using {@link SemVerOrder}. | ||
| * | ||
| * Use {@link VersionCacheLive} to provide a concrete implementation backed by | ||
| * an Effect `Ref` of a `SortedSet`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer, VersionCache, VersionCacheLive, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const cache = yield* VersionCache; | ||
| * const v1 = new SemVer({ major: 1, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * const v2 = new SemVer({ major: 2, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * yield* cache.load([v1, v2]); | ||
| * const latest = yield* cache.latest(); | ||
| * console.log(latest.toString()); // "2.0.0" | ||
| * }).pipe(Effect.provide(Layer.merge(VersionCacheLive, SemVerParserLive))); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionCacheLive} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @public | ||
| */ | ||
| var VersionCache = class extends Context.Tag("semver-effect/VersionCache")() {}; | ||
| //#endregion | ||
| export { VersionCache }; |
| import { Context } from "effect"; | ||
| //#region src/services/VersionFetcher.ts | ||
| /** | ||
| * Effect service interface for fetching available versions from an external source. | ||
| * | ||
| * Implementations might query a package registry (e.g., npm, GitHub Releases) and | ||
| * return parsed {@link SemVer} instances. This is an interface-only service; no | ||
| * default live layer is provided by this library -- consumers must supply their own. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import type { VersionFetcher } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * | ||
| * // Example: a stub implementation | ||
| * const StubFetcher = Layer.succeed(VersionFetcher, { | ||
| * fetch: (_packageName) => Effect.succeed([]), | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionFetchError} | ||
| * @see {@link VersionCache} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @public | ||
| */ | ||
| var VersionFetcher = class extends Context.Tag("semver-effect/VersionFetcher")() {}; | ||
| //#endregion | ||
| export { VersionFetcher }; |
+195
| import { UnsatisfiableConstraintError } from "../errors/UnsatisfiableConstraintError.js"; | ||
| import { Range } from "../schemas/Range.js"; | ||
| import { SemVerOrder } from "./order.js"; | ||
| import { Effect, Function } from "effect"; | ||
| //#region src/utils/algebra.ts | ||
| const makeRange = (sets) => new Range({ sets: sets.map((s) => [...s]) }); | ||
| const isSetSatisfiable = (set) => { | ||
| const lowers = []; | ||
| const uppers = []; | ||
| const equals = []; | ||
| for (const c of set) if (c.operator === "=") equals.push(c); | ||
| else if (c.operator === ">" || c.operator === ">=") lowers.push(c); | ||
| else uppers.push(c); | ||
| for (const eq of equals) for (const c of set) { | ||
| if (c === eq) continue; | ||
| const cmp = SemVerOrder(eq.version, c.version); | ||
| switch (c.operator) { | ||
| case ">": | ||
| if (cmp <= 0) return false; | ||
| break; | ||
| case ">=": | ||
| if (cmp < 0) return false; | ||
| break; | ||
| case "<": | ||
| if (cmp >= 0) return false; | ||
| break; | ||
| case "<=": | ||
| if (cmp > 0) return false; | ||
| break; | ||
| case "=": | ||
| if (cmp !== 0) return false; | ||
| break; | ||
| } | ||
| } | ||
| for (const lo of lowers) for (const hi of uppers) { | ||
| const cmp = SemVerOrder(lo.version, hi.version); | ||
| if (lo.operator === ">=" && hi.operator === "<") { | ||
| if (cmp >= 0) return false; | ||
| } else if (lo.operator === ">=" && hi.operator === "<=") { | ||
| if (cmp > 0) return false; | ||
| } else if (lo.operator === ">" && hi.operator === "<") { | ||
| if (cmp >= 0) return false; | ||
| } else if (lo.operator === ">" && hi.operator === "<=") { | ||
| if (cmp >= 0) return false; | ||
| } | ||
| } | ||
| return true; | ||
| }; | ||
| /** | ||
| * Combine two {@link Range}s with OR semantics (union of comparator sets). | ||
| * | ||
| * The resulting range matches any version that satisfies either input range. | ||
| * Internally, this concatenates the comparator sets from both ranges. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseRange, union } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* parseRange("^1.0.0"); | ||
| * const b = yield* parseRange("^2.0.0"); | ||
| * const combined = union(a, b); | ||
| * // combined matches versions satisfying ^1.0.0 OR ^2.0.0 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link intersect} | ||
| * @see {@link equivalent} | ||
| * @public | ||
| */ | ||
| const union = Function.dual(2, (a, b) => makeRange([...a.sets, ...b.sets])); | ||
| /** | ||
| * Compute the intersection of two {@link Range}s using a cross-product of | ||
| * their comparator sets. | ||
| * | ||
| * The resulting range matches only versions that satisfy both input ranges. | ||
| * Returns an `Effect.Effect` that fails with | ||
| * {@link UnsatisfiableConstraintError} when no satisfiable comparator set | ||
| * remains after intersection. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseRange, intersect } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* parseRange(">=1.0.0"); | ||
| * const b = yield* parseRange("<2.0.0"); | ||
| * const both = yield* intersect(a, b); | ||
| * // both matches >=1.0.0 AND <2.0.0 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link union} | ||
| * @see {@link UnsatisfiableConstraintError} | ||
| * @public | ||
| */ | ||
| const intersect = Function.dual(2, (a, b) => { | ||
| const candidates = []; | ||
| for (const setA of a.sets) for (const setB of b.sets) { | ||
| const merged = [...setA, ...setB]; | ||
| if (isSetSatisfiable(merged)) candidates.push(merged); | ||
| } | ||
| if (candidates.length === 0) return Effect.fail(new UnsatisfiableConstraintError({ constraints: [a, b] })); | ||
| return Effect.succeed(makeRange(candidates)); | ||
| }); | ||
| const isComparatorImplied = (set, comp) => { | ||
| for (const s of set) { | ||
| const cmp = SemVerOrder(s.version, comp.version); | ||
| switch (comp.operator) { | ||
| case ">=": | ||
| if (s.operator === ">=" && cmp >= 0 || s.operator === ">" && cmp >= 0) return true; | ||
| if (s.operator === "=" && cmp >= 0) return true; | ||
| break; | ||
| case ">": | ||
| if (s.operator === ">" && cmp >= 0) return true; | ||
| if (s.operator === ">=" && cmp > 0) return true; | ||
| if (s.operator === "=" && cmp > 0) return true; | ||
| break; | ||
| case "<=": | ||
| if (s.operator === "<=" && cmp <= 0 || s.operator === "<" && cmp <= 0) return true; | ||
| if (s.operator === "=" && cmp <= 0) return true; | ||
| break; | ||
| case "<": | ||
| if (s.operator === "<" && cmp <= 0) return true; | ||
| if (s.operator === "<=" && cmp < 0) return true; | ||
| if (s.operator === "=" && cmp < 0) return true; | ||
| break; | ||
| case "=": | ||
| if (s.operator === "=" && cmp === 0) return true; | ||
| break; | ||
| } | ||
| } | ||
| return false; | ||
| }; | ||
| const isComparatorSetSubset = (sub, sup) => { | ||
| for (const supComp of sup) if (!isComparatorImplied(sub, supComp)) return false; | ||
| return true; | ||
| }; | ||
| /** | ||
| * Check whether every version matched by `sub` is also matched by `sup`. | ||
| * | ||
| * Returns `true` if the `sub` range is a subset of (i.e., fully contained by) | ||
| * the `sup` range. This is determined by checking that every comparator set in | ||
| * `sub` is implied by at least one comparator set in `sup`. | ||
| * | ||
| * @remarks | ||
| * This check is a conservative approximation: it may return `false` for ranges | ||
| * that are technically subsets when the sub-range straddles comparator-set | ||
| * boundaries in the sup-range. For example, `>=1.0.0 <3.0.0` is a subset of | ||
| * `>=1.0.0 <2.0.0 || >=2.0.0 <3.0.0`, but `isSubset` returns `false` because | ||
| * no single sup-set fully implies the sub-set. This is a known limitation; | ||
| * false negatives are safe (they prevent incorrect simplification). | ||
| * | ||
| * @see {@link equivalent} | ||
| * @see {@link intersect} | ||
| * @public | ||
| */ | ||
| const isSubset = Function.dual(2, (sub, sup) => { | ||
| for (const subSet of sub.sets) if (!sup.sets.some((supSet) => isComparatorSetSubset(subSet, supSet))) return false; | ||
| return true; | ||
| }); | ||
| /** | ||
| * Test whether two {@link Range}s are semantically equivalent. | ||
| * | ||
| * Two ranges are equivalent when each is a {@link isSubset | subset} of the other, | ||
| * meaning they match exactly the same set of versions. | ||
| * | ||
| * @see {@link isSubset} | ||
| * @public | ||
| */ | ||
| const equivalent = Function.dual(2, (a, b) => isSubset(a, b) && isSubset(b, a)); | ||
| /** | ||
| * Remove redundant comparator sets from a {@link Range}. | ||
| * | ||
| * A comparator set is considered redundant if another set in the range is a | ||
| * subset of it (i.e., the other set is more restrictive and already covered). | ||
| * Returns a new range with only the non-redundant sets. | ||
| * | ||
| * @see {@link isSubset} | ||
| * @see {@link equivalent} | ||
| * @public | ||
| */ | ||
| const simplify = (range) => { | ||
| const sets = range.sets.filter((set, i) => { | ||
| return !range.sets.some((other, j) => i !== j && isComparatorSetSubset(set, other)); | ||
| }); | ||
| if (sets.length === 0) return range; | ||
| return makeRange(sets); | ||
| }; | ||
| //#endregion | ||
| export { equivalent, intersect, isSubset, simplify, union }; |
| //#region src/utils/bump.ts | ||
| /** | ||
| * Increment the major version and reset minor, patch, and prerelease to zero/empty. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.2.3"); | ||
| * console.log(v.bump.major().toString()); // "2.0.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link bumpMinor} | ||
| * @see {@link bumpPatch} | ||
| * @public | ||
| */ | ||
| const bumpMajor = (v) => v.bump.major(); | ||
| /** | ||
| * Increment the minor version and reset patch and prerelease to zero/empty. | ||
| * | ||
| * @see {@link bumpMajor} | ||
| * @see {@link bumpPatch} | ||
| * @public | ||
| */ | ||
| const bumpMinor = (v) => v.bump.minor(); | ||
| /** | ||
| * Increment the patch version and clear prerelease identifiers. | ||
| * | ||
| * @see {@link bumpMajor} | ||
| * @see {@link bumpMinor} | ||
| * @public | ||
| */ | ||
| const bumpPatch = (v) => v.bump.patch(); | ||
| /** | ||
| * Increment the prerelease portion of a version. | ||
| * | ||
| * @param v - The version to bump. | ||
| * @param id - Optional prerelease identifier prefix (e.g., `"alpha"`, `"beta"`). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.0.0-alpha.3"); | ||
| * console.log(v.bump.prerelease("alpha").toString()); // "1.0.0-alpha.4" | ||
| * console.log(v.bump.prerelease("beta").toString()); // "1.0.0-beta.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link bumpRelease} | ||
| * @public | ||
| */ | ||
| const bumpPrerelease = (v, id) => v.bump.prerelease(id); | ||
| /** | ||
| * Strip prerelease and build metadata, promoting a prerelease to its release version. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.2.3-rc.1"); | ||
| * console.log(v.bump.release().toString()); // "1.2.3" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link bumpPrerelease} | ||
| * @public | ||
| */ | ||
| const bumpRelease = (v) => v.bump.release(); | ||
| //#endregion | ||
| export { bumpMajor, bumpMinor, bumpPatch, bumpPrerelease, bumpRelease }; |
+212
| import { SemVer } from "../schemas/SemVer.js"; | ||
| import { SemVerOrder, SemVerOrderWithBuild } from "./order.js"; | ||
| import { Equal, Function, Option } from "effect"; | ||
| //#region src/utils/compare.ts | ||
| /** | ||
| * Compare two {@link SemVer} versions according to SemVer 2.0.0 precedence. | ||
| * | ||
| * Returns `-1` if `self` is lower, `0` if equal, `1` if higher. Build metadata | ||
| * is ignored per the spec. Supports both data-last and data-first calling styles. | ||
| * | ||
| * For the instance method alternative, use `v.compare(other)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* SemVer.parse("1.0.0"); | ||
| * const b = yield* SemVer.parse("2.0.0"); | ||
| * console.log(a.compare(b)); // -1 (instance method) | ||
| * console.log(compare(a, b)); // -1 (standalone function) | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVerOrder} | ||
| * @see {@link https://semver.org/#spec-item-11 | SemVer 2.0.0 Section 11} | ||
| * @public | ||
| */ | ||
| const compare = Function.dual(2, (self, that) => SemVerOrder(self, that)); | ||
| /** | ||
| * Test whether two {@link SemVer} versions are equal. | ||
| * | ||
| * Uses Effect structural equality (`Equal.equals`), which compares | ||
| * `major.minor.patch` and prerelease identifiers but ignores build metadata. | ||
| * | ||
| * For the instance method alternative, use `v.eq(other)`. | ||
| * | ||
| * @see {@link neq} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| const equal = Function.dual(2, (self, that) => Equal.equals(self, that)); | ||
| /** | ||
| * Test whether `self` is strictly greater than `that`. | ||
| * | ||
| * For the instance method alternative, use `v.gt(other)`. | ||
| * | ||
| * @see {@link gte} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| const gt = Function.dual(2, (self, that) => SemVerOrder(self, that) === 1); | ||
| /** | ||
| * Test whether `self` is greater than or equal to `that`. | ||
| * | ||
| * For the instance method alternative, use `v.gte(other)`. | ||
| * | ||
| * @see {@link gt} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| const gte = Function.dual(2, (self, that) => SemVerOrder(self, that) >= 0); | ||
| /** | ||
| * Test whether `self` is strictly less than `that`. | ||
| * | ||
| * For the instance method alternative, use `v.lt(other)`. | ||
| * | ||
| * @see {@link lte} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| const lt = Function.dual(2, (self, that) => SemVerOrder(self, that) === -1); | ||
| /** | ||
| * Test whether `self` is less than or equal to `that`. | ||
| * | ||
| * For the instance method alternative, use `v.lte(other)`. | ||
| * | ||
| * @see {@link lt} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| const lte = Function.dual(2, (self, that) => SemVerOrder(self, that) <= 0); | ||
| /** | ||
| * Test whether two {@link SemVer} versions are not equal. | ||
| * | ||
| * For the instance method alternative, use `v.neq(other)`. | ||
| * | ||
| * @see {@link equal} | ||
| * @public | ||
| */ | ||
| const neq = Function.dual(2, (self, that) => !Equal.equals(self, that)); | ||
| /** | ||
| * Test whether a version has prerelease identifiers. | ||
| * | ||
| * For the instance getter alternative, use `v.isPrerelease`. | ||
| * | ||
| * @see {@link isStable} | ||
| * @public | ||
| */ | ||
| const isPrerelease = (v) => v.prerelease.length > 0; | ||
| /** | ||
| * Test whether a version is a stable release (no prerelease identifiers). | ||
| * | ||
| * For the instance getter alternative, use `v.isStable`. | ||
| * | ||
| * @see {@link isPrerelease} | ||
| * @public | ||
| */ | ||
| const isStable = (v) => v.prerelease.length === 0; | ||
| /** | ||
| * Strip prerelease and/or build metadata from a version. | ||
| * | ||
| * - `"prerelease"` removes both prerelease identifiers and build metadata. | ||
| * - `"build"` removes only build metadata, preserving prerelease identifiers. | ||
| * | ||
| * Returns a new {@link SemVer} instance; the original is not mutated. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseValidSemVer, truncate } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* parseValidSemVer("1.2.3-alpha.1+build"); | ||
| * console.log(truncate(v, "prerelease").toString()); // "1.2.3" | ||
| * console.log(truncate(v, "build").toString()); // "1.2.3-alpha.1" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @public | ||
| */ | ||
| const truncate = Function.dual(2, (v, level) => level === "prerelease" ? new SemVer({ | ||
| major: v.major, | ||
| minor: v.minor, | ||
| patch: v.patch, | ||
| prerelease: [], | ||
| build: [] | ||
| }) : new SemVer({ | ||
| major: v.major, | ||
| minor: v.minor, | ||
| patch: v.patch, | ||
| prerelease: [...v.prerelease], | ||
| build: [] | ||
| })); | ||
| /** | ||
| * Sort an array of {@link SemVer} versions in ascending order. | ||
| * | ||
| * Returns a new array; the input is not mutated. | ||
| * | ||
| * @see {@link rsort} | ||
| * @see {@link SemVerOrder} | ||
| * @public | ||
| */ | ||
| const sort = (versions) => [...versions].sort(SemVerOrder); | ||
| /** | ||
| * Sort an array of {@link SemVer} versions in descending order. | ||
| * | ||
| * Returns a new array; the input is not mutated. | ||
| * | ||
| * @see {@link sort} | ||
| * @see {@link SemVerOrder} | ||
| * @public | ||
| */ | ||
| const rsort = (versions) => [...versions].sort((a, b) => SemVerOrder(b, a)); | ||
| /** | ||
| * Find the highest version in an array. | ||
| * | ||
| * Returns `Option.none()` for an empty array. | ||
| * | ||
| * @see {@link min} | ||
| * @see {@link sort} | ||
| * @public | ||
| */ | ||
| const max = (versions) => { | ||
| if (versions.length === 0) return Option.none(); | ||
| let result = versions[0]; | ||
| for (let i = 1; i < versions.length; i++) if (SemVerOrder(versions[i], result) === 1) result = versions[i]; | ||
| return Option.some(result); | ||
| }; | ||
| /** | ||
| * Find the lowest version in an array. | ||
| * | ||
| * Returns `Option.none()` for an empty array. | ||
| * | ||
| * @see {@link max} | ||
| * @see {@link sort} | ||
| * @public | ||
| */ | ||
| const min = (versions) => { | ||
| if (versions.length === 0) return Option.none(); | ||
| let result = versions[0]; | ||
| for (let i = 1; i < versions.length; i++) if (SemVerOrder(versions[i], result) === -1) result = versions[i]; | ||
| return Option.some(result); | ||
| }; | ||
| /** | ||
| * Compare two versions including build metadata. | ||
| * | ||
| * This extends the standard {@link compare} by additionally comparing build | ||
| * metadata lexicographically when versions are otherwise equal. Per SemVer 2.0.0, | ||
| * build metadata SHOULD be ignored for precedence, but this function is provided | ||
| * for cases where a total ordering including build metadata is needed. | ||
| * | ||
| * @see {@link compare} | ||
| * @see {@link SemVerOrderWithBuild} | ||
| * @public | ||
| */ | ||
| const compareWithBuild = Function.dual(2, (self, that) => SemVerOrderWithBuild(self, that)); | ||
| //#endregion | ||
| export { compare, compareWithBuild, equal, gt, gte, isPrerelease, isStable, lt, lte, max, min, neq, rsort, sort, truncate }; |
| import { SemVer } from "../schemas/SemVer.js"; | ||
| import { Comparator } from "../schemas/Comparator.js"; | ||
| //#region src/utils/desugar.ts | ||
| const sv = (major, minor, patch, prerelease = [], build = []) => new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: prerelease.slice(), | ||
| build: build.slice() | ||
| }); | ||
| const comp = (operator, version) => new Comparator({ | ||
| operator, | ||
| version | ||
| }); | ||
| const desugarTilde = (p) => { | ||
| const major = p.major ?? 0; | ||
| const minor = p.minor; | ||
| const patch = p.patch ?? 0; | ||
| if (minor === null) return [comp(">=", sv(major, 0, 0)), comp("<", sv(major + 1, 0, 0, [0]))]; | ||
| return [comp(">=", sv(major, minor, patch, p.prerelease)), comp("<", sv(major, minor + 1, 0, [0]))]; | ||
| }; | ||
| const desugarCaret = (p) => { | ||
| const major = p.major ?? 0; | ||
| const minor = p.minor; | ||
| const patch = p.patch; | ||
| const lower = sv(major, minor ?? 0, patch ?? 0, p.prerelease); | ||
| if (major !== 0) return [comp(">=", lower), comp("<", sv(major + 1, 0, 0, [0]))]; | ||
| if (minor === null) return [comp(">=", lower), comp("<", sv(1, 0, 0, [0]))]; | ||
| if (minor !== 0) return [comp(">=", lower), comp("<", sv(0, minor + 1, 0, [0]))]; | ||
| if (patch === null) return [comp(">=", lower), comp("<", sv(0, 1, 0, [0]))]; | ||
| if (patch !== 0) return [comp(">=", lower), comp("<", sv(0, 0, patch + 1, [0]))]; | ||
| return [comp(">=", lower), comp("<", sv(0, 0, 1, [0]))]; | ||
| }; | ||
| const desugarXRange = (operator, p) => { | ||
| const major = p.major; | ||
| const minor = p.minor; | ||
| const patch = p.patch; | ||
| if (major !== null && minor !== null && patch !== null) { | ||
| const version = sv(major, minor, patch, p.prerelease, p.build); | ||
| if (operator === null || operator === "=") return [comp("=", version)]; | ||
| return [comp(operator, version)]; | ||
| } | ||
| if (major === null) { | ||
| if (operator === null || operator === "" || operator === "=") return [comp(">=", sv(0, 0, 0))]; | ||
| return [comp(">=", sv(0, 0, 0))]; | ||
| } | ||
| if (minor === null) { | ||
| if (operator === null || operator === "" || operator === "=") return [comp(">=", sv(major, 0, 0)), comp("<", sv(major + 1, 0, 0, [0]))]; | ||
| if (operator === ">") return [comp(">=", sv(major + 1, 0, 0))]; | ||
| if (operator === ">=") return [comp(">=", sv(major, 0, 0))]; | ||
| if (operator === "<") return [comp("<", sv(major, 0, 0))]; | ||
| if (operator === "<=") return [comp("<", sv(major + 1, 0, 0, [0]))]; | ||
| } | ||
| if (operator === null || operator === "" || operator === "=") return [comp(">=", sv(major, minor, 0)), comp("<", sv(major, minor + 1, 0, [0]))]; | ||
| if (operator === ">") return [comp(">=", sv(major, minor + 1, 0))]; | ||
| if (operator === ">=") return [comp(">=", sv(major, minor, 0))]; | ||
| if (operator === "<") return [comp("<", sv(major, minor, 0))]; | ||
| return [comp("<", sv(major, minor + 1, 0, [0]))]; | ||
| }; | ||
| const desugarHyphen = (lower, upper) => { | ||
| const lowerVersion = sv(lower.major ?? 0, lower.minor ?? 0, lower.patch ?? 0, lower.prerelease); | ||
| if (upper.major !== null && upper.minor !== null && upper.patch !== null) { | ||
| const upperVersion = sv(upper.major, upper.minor, upper.patch, upper.prerelease); | ||
| return [comp(">=", lowerVersion), comp("<=", upperVersion)]; | ||
| } | ||
| if (upper.major !== null && upper.minor !== null) return [comp(">=", lowerVersion), comp("<", sv(upper.major, upper.minor + 1, 0, [0]))]; | ||
| if (upper.major !== null) return [comp(">=", lowerVersion), comp("<", sv(upper.major + 1, 0, 0, [0]))]; | ||
| return [comp(">=", lowerVersion)]; | ||
| }; | ||
| //#endregion | ||
| export { desugarCaret, desugarHyphen, desugarTilde, desugarXRange }; |
| import { VersionDiff } from "../schemas/VersionDiff.js"; | ||
| import { Function } from "effect"; | ||
| //#region src/utils/diff.ts | ||
| const arraysEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]); | ||
| const classifyDiff = (a, b) => { | ||
| if (a.major !== b.major) return "major"; | ||
| if (a.minor !== b.minor) return "minor"; | ||
| if (a.patch !== b.patch) return "patch"; | ||
| if (!arraysEqual(a.prerelease, b.prerelease)) return "prerelease"; | ||
| if (!arraysEqual(a.build, b.build)) return "build"; | ||
| return "none"; | ||
| }; | ||
| /** | ||
| * Compute the difference between two {@link SemVer} versions. | ||
| * | ||
| * Returns a {@link VersionDiff} containing the type of change (major, minor, | ||
| * patch, prerelease, build, or none) and signed numeric deltas for each field. | ||
| * | ||
| * Also available as `SemVer.diff(a, b)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* SemVer.parse("1.2.3"); | ||
| * const b = yield* SemVer.parse("1.3.0"); | ||
| * const d = SemVer.diff(a, b); // static method | ||
| * console.log(d.type); // "minor" | ||
| * console.log(d.minor); // 1 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionDiff} | ||
| * @public | ||
| */ | ||
| const diff = Function.dual(2, (a, b) => new VersionDiff({ | ||
| type: classifyDiff(a, b), | ||
| from: a, | ||
| to: b, | ||
| major: b.major - a.major, | ||
| minor: b.minor - a.minor, | ||
| patch: b.patch - a.patch | ||
| })); | ||
| //#endregion | ||
| export { diff }; |
+432
| import { InvalidComparatorError } from "../errors/InvalidComparatorError.js"; | ||
| import { InvalidRangeError } from "../errors/InvalidRangeError.js"; | ||
| import { InvalidVersionError } from "../errors/InvalidVersionError.js"; | ||
| import { SemVer } from "../schemas/SemVer.js"; | ||
| import { Comparator } from "../schemas/Comparator.js"; | ||
| import { Range } from "../schemas/Range.js"; | ||
| import { desugarCaret, desugarHyphen, desugarTilde, desugarXRange } from "./desugar.js"; | ||
| import { Effect } from "effect"; | ||
| //#region src/utils/grammar.ts | ||
| const peek = (s) => s.pos < s.len ? s.input[s.pos] : void 0; | ||
| const advance = (s) => { | ||
| if (s.pos < s.len) { | ||
| const ch = s.input[s.pos]; | ||
| s.pos++; | ||
| return ch; | ||
| } | ||
| }; | ||
| const isDigit = (ch) => ch >= "0" && ch <= "9"; | ||
| const isLetter = (ch) => ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z"; | ||
| const isIdentChar = (ch) => isDigit(ch) || isLetter(ch) || ch === "-"; | ||
| const atEnd = (s) => s.pos >= s.len; | ||
| const peekDigit = (s) => { | ||
| const ch = peek(s); | ||
| return ch !== void 0 && isDigit(ch); | ||
| }; | ||
| const peekIdentChar = (s) => { | ||
| const ch = peek(s); | ||
| return ch !== void 0 && isIdentChar(ch); | ||
| }; | ||
| const makeParseNumericIdentifier = (makeFail) => (s) => Effect.gen(function* () { | ||
| const start = s.pos; | ||
| const first = peek(s); | ||
| if (first === void 0 || !isDigit(first)) return yield* Effect.fail(makeFail(s)); | ||
| let digits = ""; | ||
| while (peekDigit(s)) digits += advance(s); | ||
| if (digits.length > 1 && digits[0] === "0") { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| const value = Number(digits); | ||
| if (!Number.isSafeInteger(value)) { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| return value; | ||
| }); | ||
| const makeParsePrereleaseIdentifier = (makeFail) => (s) => Effect.gen(function* () { | ||
| const start = s.pos; | ||
| let token = ""; | ||
| let hasNonDigit = false; | ||
| const first = peek(s); | ||
| if (first === void 0 || !isIdentChar(first)) return yield* Effect.fail(makeFail(s)); | ||
| while (peekIdentChar(s)) { | ||
| const ch = advance(s) ?? ""; | ||
| if (!isDigit(ch)) hasNonDigit = true; | ||
| token += ch; | ||
| } | ||
| if (token.length === 0) return yield* Effect.fail(makeFail(s)); | ||
| if (hasNonDigit) return token; | ||
| if (token.length > 1 && token[0] === "0") { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| const value = Number(token); | ||
| if (!Number.isSafeInteger(value)) { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| return value; | ||
| }); | ||
| const makeParseBuildIdentifier = (makeFail) => (s) => Effect.gen(function* () { | ||
| let token = ""; | ||
| const first = peek(s); | ||
| if (first === void 0 || !isIdentChar(first)) return yield* Effect.fail(makeFail(s)); | ||
| while (peekIdentChar(s)) token += advance(s) ?? ""; | ||
| if (token.length === 0) return yield* Effect.fail(makeFail(s)); | ||
| return token; | ||
| }); | ||
| const makeParsePreRelease = (parsePrereleaseId) => (s) => Effect.gen(function* () { | ||
| const identifiers = []; | ||
| identifiers.push(yield* parsePrereleaseId(s)); | ||
| while (!atEnd(s) && peek(s) === ".") { | ||
| advance(s); | ||
| identifiers.push(yield* parsePrereleaseId(s)); | ||
| } | ||
| return identifiers; | ||
| }); | ||
| const makeParseBuild = (parseBuildId) => (s) => Effect.gen(function* () { | ||
| const identifiers = []; | ||
| identifiers.push(yield* parseBuildId(s)); | ||
| while (!atEnd(s) && peek(s) === ".") { | ||
| advance(s); | ||
| identifiers.push(yield* parseBuildId(s)); | ||
| } | ||
| return identifiers; | ||
| }); | ||
| const failVersion = (s, position) => new InvalidVersionError({ | ||
| input: s.input, | ||
| position: position ?? s.pos | ||
| }); | ||
| const parseNumericIdentifier = makeParseNumericIdentifier(failVersion); | ||
| const parsePrereleaseIdentifier = makeParsePrereleaseIdentifier(failVersion); | ||
| const parseBuildIdentifier = makeParseBuildIdentifier(failVersion); | ||
| const parsePreRelease = makeParsePreRelease(parsePrereleaseIdentifier); | ||
| const parseBuild = makeParseBuild(parseBuildIdentifier); | ||
| /** | ||
| * Parse a string into a {@link SemVer} using a strict SemVer 2.0.0 | ||
| * recursive-descent parser. | ||
| * | ||
| * Rejects `v`/`V` prefixes, `=` prefixes, leading zeros on numeric identifiers, | ||
| * and any input that does not fully consume as a valid `major.minor.patch[-prerelease][+build]` | ||
| * string. Returns an `Effect.Effect` that fails with {@link InvalidVersionError} | ||
| * on invalid input. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseValidSemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* parseValidSemVer("1.2.3-beta.1+build.42"); | ||
| * console.log(v.toString()); // "1.2.3-beta.1+build.42" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVer} | ||
| * @see {@link InvalidVersionError} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| const parseValidSemVer = (raw) => Effect.gen(function* () { | ||
| const trimmed = raw.trim(); | ||
| const s = { | ||
| input: trimmed, | ||
| pos: 0, | ||
| len: trimmed.length | ||
| }; | ||
| if (s.len === 0) return yield* Effect.fail(new InvalidVersionError({ | ||
| input: raw, | ||
| position: 0 | ||
| })); | ||
| const first = peek(s); | ||
| if (first === "v" || first === "V") return yield* Effect.fail(new InvalidVersionError({ | ||
| input: trimmed, | ||
| position: 0 | ||
| })); | ||
| if (first === "=") return yield* Effect.fail(new InvalidVersionError({ | ||
| input: trimmed, | ||
| position: 0 | ||
| })); | ||
| const major = yield* parseNumericIdentifier(s); | ||
| if (peek(s) !== ".") return yield* Effect.fail(failVersion(s)); | ||
| advance(s); | ||
| const minor = yield* parseNumericIdentifier(s); | ||
| if (peek(s) !== ".") return yield* Effect.fail(failVersion(s)); | ||
| advance(s); | ||
| const patch = yield* parseNumericIdentifier(s); | ||
| let prerelease = []; | ||
| if (!atEnd(s) && peek(s) === "-") { | ||
| advance(s); | ||
| prerelease = yield* parsePreRelease(s); | ||
| } | ||
| let build = []; | ||
| if (!atEnd(s) && peek(s) === "+") { | ||
| advance(s); | ||
| build = yield* parseBuild(s); | ||
| } | ||
| if (!atEnd(s)) return yield* Effect.fail(failVersion(s)); | ||
| return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease, | ||
| build | ||
| }); | ||
| }); | ||
| const failRange = (s, position) => new InvalidRangeError({ | ||
| input: s.input, | ||
| position: position ?? s.pos | ||
| }); | ||
| const parseNumericIdentifierRange = makeParseNumericIdentifier(failRange); | ||
| const parsePrereleaseIdentifierRange = makeParsePrereleaseIdentifier(failRange); | ||
| const parseBuildIdentifierRange = makeParseBuildIdentifier(failRange); | ||
| const parsePrereleaseRange = makeParsePreRelease(parsePrereleaseIdentifierRange); | ||
| const parseBuildRange = makeParseBuild(parseBuildIdentifierRange); | ||
| const parseXR = (s) => Effect.gen(function* () { | ||
| const ch = peek(s); | ||
| if (ch === "x" || ch === "X" || ch === "*") { | ||
| advance(s); | ||
| return null; | ||
| } | ||
| return yield* parseNumericIdentifierRange(s); | ||
| }); | ||
| const parsePartial = (s) => Effect.gen(function* () { | ||
| const major = yield* parseXR(s); | ||
| let minor = null; | ||
| let patch = null; | ||
| let prerelease = []; | ||
| let build = []; | ||
| if (!atEnd(s) && peek(s) === ".") { | ||
| advance(s); | ||
| minor = yield* parseXR(s); | ||
| if (!atEnd(s) && peek(s) === ".") { | ||
| advance(s); | ||
| patch = yield* parseXR(s); | ||
| if (patch !== null && !atEnd(s) && peek(s) === "-") { | ||
| advance(s); | ||
| prerelease = yield* parsePrereleaseRange(s); | ||
| } | ||
| if (patch !== null && !atEnd(s) && peek(s) === "+") { | ||
| advance(s); | ||
| build = yield* parseBuildRange(s); | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease, | ||
| build | ||
| }; | ||
| }); | ||
| const parseOperator = (s) => { | ||
| const ch = peek(s); | ||
| if (ch === ">") { | ||
| advance(s); | ||
| if (peek(s) === "=") { | ||
| advance(s); | ||
| return ">="; | ||
| } | ||
| return ">"; | ||
| } | ||
| if (ch === "<") { | ||
| advance(s); | ||
| if (peek(s) === "=") { | ||
| advance(s); | ||
| return "<="; | ||
| } | ||
| return "<"; | ||
| } | ||
| if (ch === "=") { | ||
| advance(s); | ||
| return "="; | ||
| } | ||
| return null; | ||
| }; | ||
| const skipSpaces = (s) => { | ||
| while (!atEnd(s) && peek(s) === " ") advance(s); | ||
| }; | ||
| const isHyphenRange = (s) => s.pos + 2 < s.len && s.input[s.pos] === " " && s.input[s.pos + 1] === "-" && s.input[s.pos + 2] === " "; | ||
| const isOrSeparator = (s) => { | ||
| let pos = s.pos; | ||
| while (pos < s.len && s.input[pos] === " ") pos++; | ||
| return pos + 1 < s.len && s.input[pos] === "|" && s.input[pos + 1] === "|"; | ||
| }; | ||
| const consumeOrSeparator = (s) => { | ||
| while (!atEnd(s) && peek(s) === " ") advance(s); | ||
| advance(s); | ||
| advance(s); | ||
| while (!atEnd(s) && peek(s) === " ") advance(s); | ||
| }; | ||
| const parseSimple = (s) => Effect.gen(function* () { | ||
| const ch = peek(s); | ||
| if (ch === "~") { | ||
| advance(s); | ||
| if (peek(s) === ">") return yield* Effect.fail(failRange(s)); | ||
| return desugarTilde(yield* parsePartial(s)); | ||
| } | ||
| if (ch === "^") { | ||
| advance(s); | ||
| return desugarCaret(yield* parsePartial(s)); | ||
| } | ||
| return desugarXRange(parseOperator(s), yield* parsePartial(s)); | ||
| }); | ||
| const atRangeEnd = (s) => { | ||
| if (atEnd(s)) return true; | ||
| let pos = s.pos; | ||
| while (pos < s.len && s.input[pos] === " ") pos++; | ||
| if (pos + 1 < s.len && s.input[pos] === "|" && s.input[pos + 1] === "|") return true; | ||
| return false; | ||
| }; | ||
| const parseRangeComparators = (s) => Effect.gen(function* () { | ||
| skipSpaces(s); | ||
| const savedPos = s.pos; | ||
| const tryHyphen = yield* Effect.either(Effect.gen(function* () { | ||
| const lower = yield* parsePartial(s); | ||
| if (!isHyphenRange(s)) return yield* Effect.fail(failRange(s)); | ||
| advance(s); | ||
| advance(s); | ||
| advance(s); | ||
| return desugarHyphen(lower, yield* parsePartial(s)); | ||
| })); | ||
| if (tryHyphen._tag === "Right") return tryHyphen.right; | ||
| s.pos = savedPos; | ||
| const comparators = []; | ||
| const first = yield* parseSimple(s); | ||
| for (const c of first) comparators.push(c); | ||
| while (!atRangeEnd(s)) { | ||
| if (peek(s) !== " ") break; | ||
| skipSpaces(s); | ||
| if (atRangeEnd(s)) break; | ||
| const next = yield* parseSimple(s); | ||
| for (const c of next) comparators.push(c); | ||
| } | ||
| return comparators; | ||
| }); | ||
| const parseRangeSet = (raw) => Effect.gen(function* () { | ||
| const trimmed = raw.trim(); | ||
| if (trimmed.length === 0) return new Range({ sets: [desugarXRange(null, { | ||
| major: null, | ||
| minor: null, | ||
| patch: null, | ||
| prerelease: [], | ||
| build: [] | ||
| })] }); | ||
| const s = { | ||
| input: trimmed, | ||
| pos: 0, | ||
| len: trimmed.length | ||
| }; | ||
| const sets = []; | ||
| const first = yield* parseRangeComparators(s); | ||
| sets.push(first); | ||
| while (!atEnd(s)) if (isOrSeparator(s)) { | ||
| consumeOrSeparator(s); | ||
| const next = yield* parseRangeComparators(s); | ||
| sets.push(next); | ||
| } else break; | ||
| if (!atEnd(s)) return yield* Effect.fail(failRange(s)); | ||
| return new Range({ sets }); | ||
| }); | ||
| /** | ||
| * Parse a string into a single {@link Comparator}. | ||
| * | ||
| * Accepts an optional operator prefix (`=`, `>`, `>=`, `<`, `<=`) followed by | ||
| * a complete `major.minor.patch[-prerelease][+build]` version string. Wildcards | ||
| * and range syntax (tilde, caret, hyphen, X-range) are not allowed. | ||
| * | ||
| * If no operator is specified, `"="` (exact match) is assumed. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseSingleComparator } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const comp = yield* parseSingleComparator(">=1.0.0"); | ||
| * console.log(comp.operator); // ">=" | ||
| * console.log(comp.version.toString()); // "1.0.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Comparator} | ||
| * @see {@link InvalidComparatorError} | ||
| * @public | ||
| */ | ||
| const parseSingleComparator = (raw) => Effect.gen(function* () { | ||
| const trimmed = raw.trim(); | ||
| if (trimmed.length === 0) return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: raw, | ||
| position: 0 | ||
| })); | ||
| const s = { | ||
| input: trimmed, | ||
| pos: 0, | ||
| len: trimmed.length | ||
| }; | ||
| const operator = parseOperator(s); | ||
| const ch = peek(s); | ||
| if (ch === ">" || ch === "<" || ch === "=") return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| const major = yield* parseNumericIdentifierRange(s).pipe(Effect.mapError(() => new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| if (peek(s) !== ".") return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| advance(s); | ||
| const minor = yield* parseNumericIdentifierRange(s).pipe(Effect.mapError(() => new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| if (peek(s) !== ".") return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| advance(s); | ||
| const patch = yield* parseNumericIdentifierRange(s).pipe(Effect.mapError(() => new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| let prerelease = []; | ||
| if (!atEnd(s) && peek(s) === "-") { | ||
| advance(s); | ||
| prerelease = yield* parsePrereleaseRange(s).pipe(Effect.mapError(() => new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| } | ||
| let build = []; | ||
| if (!atEnd(s) && peek(s) === "+") { | ||
| advance(s); | ||
| build = yield* parseBuildRange(s).pipe(Effect.mapError(() => new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| } | ||
| if (!atEnd(s)) return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| const version = new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease, | ||
| build | ||
| }); | ||
| return new Comparator({ | ||
| operator: operator ?? "=", | ||
| version | ||
| }); | ||
| }); | ||
| //#endregion | ||
| export { parseRangeSet, parseSingleComparator, parseValidSemVer }; |
| import { Function, Option } from "effect"; | ||
| //#region src/utils/matching.ts | ||
| /** | ||
| * Test whether a {@link SemVer} version satisfies a {@link Range}. | ||
| * | ||
| * For the instance method alternative, use `range.test(version)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer, Range } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.5.0"); | ||
| * const r = yield* Range.parse("^1.0.0"); | ||
| * console.log(r.test(v)); // true (instance method) | ||
| * console.log(satisfies(v, r)); // true (standalone function) | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link filter} | ||
| * @see {@link maxSatisfying} | ||
| * @see {@link minSatisfying} | ||
| * @public | ||
| */ | ||
| const satisfies = Function.dual(2, (version, range) => range.test(version)); | ||
| /** | ||
| * Filter an array of {@link SemVer} versions to only those satisfying a {@link Range}. | ||
| * | ||
| * For the instance method alternative, use `range.filter(versions)`. | ||
| * | ||
| * @see {@link satisfies} | ||
| * @public | ||
| */ | ||
| const filter = Function.dual(2, (versions, range) => range.filter(versions)); | ||
| /** | ||
| * Find the highest {@link SemVer} version satisfying a {@link Range}. | ||
| * | ||
| * Returns `Option.none()` if no version satisfies the range. | ||
| * | ||
| * @see {@link minSatisfying} | ||
| * @see {@link satisfies} | ||
| * @public | ||
| */ | ||
| const maxSatisfying = Function.dual(2, (versions, range) => { | ||
| let best = null; | ||
| for (const v of versions) if (range.test(v)) { | ||
| if (best === null || v.compare(best) > 0) best = v; | ||
| } | ||
| return best === null ? Option.none() : Option.some(best); | ||
| }); | ||
| /** | ||
| * Find the lowest {@link SemVer} version satisfying a {@link Range}. | ||
| * | ||
| * Returns `Option.none()` if no version satisfies the range. | ||
| * | ||
| * @see {@link maxSatisfying} | ||
| * @see {@link satisfies} | ||
| * @public | ||
| */ | ||
| const minSatisfying = Function.dual(2, (versions, range) => { | ||
| let best = null; | ||
| for (const v of versions) if (range.test(v)) { | ||
| if (best === null || v.compare(best) < 0) best = v; | ||
| } | ||
| return best === null ? Option.none() : Option.some(best); | ||
| }); | ||
| //#endregion | ||
| export { filter, maxSatisfying, minSatisfying, satisfies }; |
| import { Range } from "../schemas/Range.js"; | ||
| import { SemVerOrder } from "./order.js"; | ||
| //#region src/utils/normalize.ts | ||
| const operatorWeight = (op) => { | ||
| switch (op) { | ||
| case ">=": return 0; | ||
| case ">": return 1; | ||
| case "=": return 2; | ||
| case "<": return 3; | ||
| case "<=": return 4; | ||
| default: return 5; | ||
| } | ||
| }; | ||
| const sortComparators = (set) => [...set].sort((a, b) => { | ||
| const w = operatorWeight(a.operator) - operatorWeight(b.operator); | ||
| if (w !== 0) return w; | ||
| return SemVerOrder(a.version, b.version); | ||
| }); | ||
| const removeDuplicates = (set) => { | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| return set.filter((c) => { | ||
| const v = c.version; | ||
| const pre = v.prerelease.length > 0 ? `-${v.prerelease.join(".")}` : ""; | ||
| const key = `${c.operator}${v.major}.${v.minor}.${v.patch}${pre}`; | ||
| if (seen.has(key)) return false; | ||
| seen.add(key); | ||
| return true; | ||
| }); | ||
| }; | ||
| const normalizeComparatorSet = (set) => sortComparators(removeDuplicates(set)); | ||
| const normalizeRange = (range) => new Range({ sets: range.sets.map((set) => [...normalizeComparatorSet(set)]) }); | ||
| //#endregion | ||
| export { normalizeRange }; |
| import { Order } from "effect"; | ||
| //#region src/utils/order.ts | ||
| /** | ||
| * Effect `Order.Order` instance for {@link SemVer} following SemVer 2.0.0 | ||
| * precedence rules. Delegates to `SemVer`'s `compare` instance method. | ||
| * | ||
| * @see {@link SemVerOrderWithBuild} | ||
| * @see {@link https://semver.org/#spec-item-11 | SemVer 2.0.0 Section 11} | ||
| * @public | ||
| */ | ||
| const SemVerOrder = Order.make((a, b) => a.compare(b)); | ||
| /** | ||
| * Effect `Order.Order` instance for {@link SemVer} that additionally | ||
| * compares build metadata when versions are otherwise equal. | ||
| * | ||
| * @see {@link SemVerOrder} | ||
| * @public | ||
| */ | ||
| const SemVerOrderWithBuild = Order.make((a, b) => { | ||
| const base = a.compare(b); | ||
| if (base !== 0) return base; | ||
| const aHasBuild = a.build.length > 0; | ||
| const bHasBuild = b.build.length > 0; | ||
| if (!aHasBuild && bHasBuild) return -1; | ||
| if (aHasBuild && !bHasBuild) return 1; | ||
| const len = Math.min(a.build.length, b.build.length); | ||
| for (let i = 0; i < len; i++) { | ||
| if (a.build[i] < b.build[i]) return -1; | ||
| if (a.build[i] > b.build[i]) return 1; | ||
| } | ||
| if (a.build.length !== b.build.length) return a.build.length < b.build.length ? -1 : 1; | ||
| return 0; | ||
| }); | ||
| //#endregion | ||
| export { SemVerOrder, SemVerOrderWithBuild }; |
| import { parseRangeSet } from "./grammar.js"; | ||
| import { normalizeRange } from "./normalize.js"; | ||
| import { Effect } from "effect"; | ||
| //#region src/utils/parseRange.ts | ||
| /** | ||
| * Parse a SemVer range expression string and normalize the result. | ||
| * | ||
| * This is a convenience function that performs the same parsing and normalization | ||
| * as `SemVerParser`'s `parseRange` method but without requiring the service in scope. | ||
| * Returns an `Effect.Effect` that fails with {@link InvalidRangeError} | ||
| * on invalid input. | ||
| * | ||
| * Supports hyphen ranges (`1.0.0 - 2.0.0`), X-ranges (`1.x`, `*`), tilde | ||
| * ranges (`~1.2.3`), caret ranges (`^1.2.3`), and `||`-separated unions. | ||
| * All version components must be strictly valid SemVer 2.0.0 (no loose parsing). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseRange, parseValidSemVer, satisfies } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const range = yield* parseRange("^1.0.0"); | ||
| * const v = yield* parseValidSemVer("1.5.0"); | ||
| * console.log(satisfies(v, range)); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Range} | ||
| * @see {@link SemVerParser} | ||
| * @see {@link InvalidRangeError} | ||
| * @public | ||
| */ | ||
| const parseRange = (input) => Effect.map(parseRangeSet(input), normalizeRange); | ||
| //#endregion | ||
| export { parseRange }; |
| import { Match } from "effect"; | ||
| //#region src/utils/prettyPrint.ts | ||
| const matcher = Match.type().pipe(Match.tag("SemVer", (sv) => sv.toString()), Match.tag("Comparator", (c) => c.toString()), Match.tag("Range", (r) => r.toString()), Match.tag("VersionDiff", (d) => d.toString()), Match.exhaustive); | ||
| /** | ||
| * Convert any {@link Printable} schema value to its human-readable string form. | ||
| * | ||
| * Dispatches on the `_tag` field to call the appropriate `toString()` method. | ||
| * This is a convenience for logging and display; each schema type also has its | ||
| * own `toString()` method. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { prettyPrint, parseValidSemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* parseValidSemVer("1.2.3-alpha.1"); | ||
| * console.log(prettyPrint(v)); // "1.2.3-alpha.1" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Printable} | ||
| * @public | ||
| */ | ||
| const prettyPrint = (value) => matcher(value); | ||
| //#endregion | ||
| export { prettyPrint }; |
+1006
-1024
@@ -0,162 +1,128 @@ | ||
| import { Context, Effect, Equal, Hash, Layer, Option, Order, Schema } from "effect"; | ||
| //#region src/errors/EmptyCacheError.d.ts | ||
| /** | ||
| * **semver-effect** — Strict SemVer 2.0.0 implementation built on Effect. | ||
| * Tagged error base for {@link EmptyCacheError}. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer, Range, satisfies } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `EmptyCacheError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use {@link EmptyCacheError} | ||
| * directly rather than referencing this base. | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.2.3"); | ||
| * const next = v.bump.minor(); // 1.3.0 | ||
| * const range = yield* Range.parse("^1.0.0"); | ||
| * console.log(range.test(v)); // true | ||
| * console.log(v.gt(next)); // false | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @see {@link https://effect.website | Effect} | ||
| * | ||
| * @packageDocumentation | ||
| * @public | ||
| */ | ||
| import { Context } from 'effect'; | ||
| import { Effect } from 'effect/Effect'; | ||
| import { Effect as Effect_2 } from 'effect'; | ||
| import { Equal } from 'effect'; | ||
| import { Hash } from 'effect'; | ||
| import { Layer } from 'effect'; | ||
| import { Option } from 'effect/Option'; | ||
| import { Option as Option_2 } from 'effect'; | ||
| import { Order } from 'effect'; | ||
| import { Schema } from 'effect'; | ||
| import { VoidIfEmpty } from 'effect/Types'; | ||
| import { YieldableError } from 'effect/Cause'; | ||
| declare const EmptyCacheErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "EmptyCacheError"; | ||
| } & Readonly<A>; | ||
| /** | ||
| * Increment the major version and reset minor, patch, and prerelease to zero/empty. | ||
| * Indicates that a {@link VersionCache} operation was attempted on an empty cache. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * Returned by query and grouping operations (e.g., `versions`, `latest`, `oldest`, | ||
| * `groupBy`) when no versions have been loaded into the cache. | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.2.3"); | ||
| * console.log(v.bump.major().toString()); // "2.0.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link bumpMinor} | ||
| * @see {@link bumpPatch} | ||
| * @see {@link VersionCache} | ||
| * @public | ||
| */ | ||
| export declare const bumpMajor: (v: SemVer) => SemVer; | ||
| declare class EmptyCacheError extends EmptyCacheErrorBase { | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/InvalidVersionError.d.ts | ||
| /** | ||
| * Increment the minor version and reset patch and prerelease to zero/empty. | ||
| * Tagged error base for {@link InvalidVersionError}. | ||
| * | ||
| * @see {@link bumpMajor} | ||
| * @see {@link bumpPatch} | ||
| */ | ||
| export declare const bumpMinor: (v: SemVer) => SemVer; | ||
| /** | ||
| * Increment the patch version and clear prerelease identifiers. | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `InvalidVersionError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link InvalidVersionError} directly rather than referencing this base. | ||
| * | ||
| * @see {@link bumpMajor} | ||
| * @see {@link bumpMinor} | ||
| * @public | ||
| */ | ||
| export declare const bumpPatch: (v: SemVer) => SemVer; | ||
| declare const InvalidVersionErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "InvalidVersionError"; | ||
| } & Readonly<A>; | ||
| /** | ||
| * Increment the prerelease portion of a version. | ||
| * Indicates that a string could not be parsed as a valid SemVer 2.0.0 version. | ||
| * | ||
| * @param v - The version to bump. | ||
| * @param id - Optional prerelease identifier prefix (e.g., `"alpha"`, `"beta"`). | ||
| * This error is returned when {@link parseValidSemVer} (or the `SemVerParser` service) | ||
| * encounters input that does not conform to the `major.minor.patch[-prerelease][+build]` | ||
| * format. Unlike node-semver, no loose parsing or `v`-prefix coercion is performed. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.0.0-alpha.3"); | ||
| * console.log(v.bump.prerelease("alpha").toString()); // "1.0.0-alpha.4" | ||
| * console.log(v.bump.prerelease("beta").toString()); // "1.0.0-beta.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link bumpRelease} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| export declare const bumpPrerelease: (v: SemVer, id?: string) => SemVer; | ||
| declare class InvalidVersionError extends InvalidVersionErrorBase<{ | ||
| /** The raw input string that failed to parse. */readonly input: string; /** The character position where parsing failed, if available. */ | ||
| readonly position?: number; | ||
| }> { | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/schemas/VersionDiff.d.ts | ||
| declare const VersionDiff_base: Schema.TaggedClass<VersionDiff, "VersionDiff", { | ||
| readonly _tag: Schema.tag<"VersionDiff">; | ||
| } & { | ||
| type: Schema.Literal<["major", "minor", "patch", "prerelease", "build", "none"]>; | ||
| from: typeof SemVer; | ||
| to: typeof SemVer; | ||
| major: typeof Schema.Number; | ||
| minor: typeof Schema.Number; | ||
| patch: typeof Schema.Number; | ||
| }>; | ||
| /** | ||
| * Strip prerelease and build metadata, promoting a prerelease to its release version. | ||
| * The result of computing the difference between two {@link SemVer} versions. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * Contains the classification of the change (`type`) along with the signed | ||
| * numeric deltas for `major`, `minor`, and `patch` fields. The `from` and `to` | ||
| * fields preserve the original version objects. | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.2.3-rc.1"); | ||
| * console.log(v.bump.release().toString()); // "1.2.3" | ||
| * }); | ||
| * ``` | ||
| * The `type` field indicates the highest-precedence field that differs: | ||
| * - `"major"` — major versions differ | ||
| * - `"minor"` — major is equal, minor versions differ | ||
| * - `"patch"` — major and minor are equal, patch versions differ | ||
| * - `"prerelease"` — only prerelease identifiers differ | ||
| * - `"build"` — only build metadata differs | ||
| * - `"none"` — versions are identical | ||
| * | ||
| * @see {@link bumpPrerelease} | ||
| */ | ||
| export declare const bumpRelease: (v: SemVer) => SemVer; | ||
| /** | ||
| * A single version constraint consisting of a comparison operator and a version. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { Comparator } from "semver-effect"; | ||
| * import { parseValidSemVer, diff } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const comp = yield* Comparator.parse(">=1.2.3"); | ||
| * console.log(comp.operator); // ">=" | ||
| * console.log(comp.version.toString()); // "1.2.3" | ||
| * console.log(comp.test(new SemVer({ major: 2, minor: 0, patch: 0, prerelease: [], build: [] }))); // true | ||
| * const a = yield* parseValidSemVer("1.2.3"); | ||
| * const b = yield* parseValidSemVer("2.0.0"); | ||
| * const d = diff(a, b); | ||
| * console.log(d.type); // "major" | ||
| * console.log(d.major); // 1 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Range} | ||
| * @see {@link diff} | ||
| * @see {@link SemVer} | ||
| * @public | ||
| */ | ||
| export declare class Comparator extends Comparator_base { | ||
| /** Parse a comparator string (e.g. `">=1.2.3"`). Wired at module load by index.ts. */ | ||
| static parse: (input: string) => Effect<Comparator, InvalidComparatorError>; | ||
| /** Test whether a version satisfies this comparator. */ | ||
| test(version: SemVer): boolean; | ||
| toString(): string; | ||
| declare class VersionDiff extends VersionDiff_base { | ||
| toString(): string; | ||
| toJSON(): unknown; | ||
| } | ||
| declare const Comparator_base: Schema.TaggedClass<Comparator, "Comparator", { | ||
| readonly _tag: Schema.tag<"Comparator">; | ||
| //#endregion | ||
| //#region src/schemas/SemVer.d.ts | ||
| declare const SemVer_base: Schema.TaggedClass<SemVer, "SemVer", { | ||
| readonly _tag: Schema.tag<"SemVer">; | ||
| } & { | ||
| operator: Schema.Literal<["=", ">", ">=", "<", "<="]>; | ||
| version: typeof SemVer; | ||
| major: typeof Schema.Number; | ||
| minor: typeof Schema.Number; | ||
| patch: typeof Schema.Number; | ||
| prerelease: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>; | ||
| build: Schema.Array$<typeof Schema.String>; | ||
| }>; | ||
| /** | ||
| * A comparator set: an array of {@link Comparator} instances combined with AND | ||
| * semantics. A version must satisfy every comparator in the set to match. | ||
| * A parsed SemVer 2.0.0 version, represented as an Effect `Schema.TaggedClass`. | ||
| * | ||
| * @see {@link Range} | ||
| */ | ||
| export declare type ComparatorSet = ReadonlyArray<Comparator>; | ||
| /** | ||
| * Compare two {@link SemVer} versions according to SemVer 2.0.0 precedence. | ||
| * Provides instance methods for comparison, bumping, and predicates, plus | ||
| * static methods for parsing and collection operations. | ||
| * | ||
| * Returns `-1` if `self` is lower, `0` if equal, `1` if higher. Build metadata | ||
| * is ignored per the spec. Supports both data-last and data-first calling styles. | ||
| * | ||
| * For the instance method alternative, use `v.compare(other)`. | ||
| * | ||
| * @example | ||
@@ -168,185 +134,128 @@ * ```typescript | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* SemVer.parse("1.0.0"); | ||
| * const b = yield* SemVer.parse("2.0.0"); | ||
| * console.log(a.compare(b)); // -1 (instance method) | ||
| * console.log(compare(a, b)); // -1 (standalone function) | ||
| * const v = yield* SemVer.parse("1.2.3"); | ||
| * const next = v.bump.minor(); // 1.3.0 | ||
| * console.log(v.gt(next)); // false | ||
| * console.log(next.isStable); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVerOrder} | ||
| * @see {@link https://semver.org/#spec-item-11 | SemVer 2.0.0 Section 11} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| export declare const compare: { | ||
| declare class SemVer extends SemVer_base { | ||
| /** Parse a strict SemVer 2.0.0 string. @remarks Import from `"semver-effect"`, not the schema subpath. */ | ||
| static parse: (input: string) => import("effect/Effect").Effect<SemVer, InvalidVersionError>; | ||
| /** Convenience positional constructor. Wired at module load by index.ts. */ | ||
| static of: (major: number, minor: number, patch: number, prerelease?: ReadonlyArray<string | number>, build?: ReadonlyArray<string>) => SemVer; | ||
| /** Compare two versions. Returns `-1`, `0`, or `1`. Dual API. */ | ||
| static compare: { | ||
| (that: SemVer): (self: SemVer) => -1 | 0 | 1; | ||
| (self: SemVer, that: SemVer): -1 | 0 | 1; | ||
| }; | ||
| /** | ||
| * Compare two versions including build metadata. | ||
| * | ||
| * This extends the standard {@link compare} by additionally comparing build | ||
| * metadata lexicographically when versions are otherwise equal. Per SemVer 2.0.0, | ||
| * build metadata SHOULD be ignored for precedence, but this function is provided | ||
| * for cases where a total ordering including build metadata is needed. | ||
| * | ||
| * @see {@link compare} | ||
| * @see {@link SemVerOrderWithBuild} | ||
| */ | ||
| export declare const compareWithBuild: { | ||
| (that: SemVer): (self: SemVer) => -1 | 0 | 1; | ||
| (self: SemVer, that: SemVer): -1 | 0 | 1; | ||
| }; | ||
| /** | ||
| * Compute the difference between two {@link SemVer} versions. | ||
| * | ||
| * Returns a {@link VersionDiff} containing the type of change (major, minor, | ||
| * patch, prerelease, build, or none) and signed numeric deltas for each field. | ||
| * | ||
| * Also available as `SemVer.diff(a, b)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* SemVer.parse("1.2.3"); | ||
| * const b = yield* SemVer.parse("1.3.0"); | ||
| * const d = SemVer.diff(a, b); // static method | ||
| * console.log(d.type); // "minor" | ||
| * console.log(d.minor); // 1 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionDiff} | ||
| */ | ||
| export declare const diff: { | ||
| (b: SemVer): (a: SemVer) => VersionDiff; | ||
| (a: SemVer, b: SemVer): VersionDiff; | ||
| }; | ||
| /** | ||
| * Indicates that a {@link VersionCache} operation was attempted on an empty cache. | ||
| * | ||
| * Returned by query and grouping operations (e.g., `versions`, `latest`, `oldest`, | ||
| * `groupBy`) when no versions have been loaded into the cache. | ||
| * | ||
| * @see {@link VersionCache} | ||
| */ | ||
| export declare class EmptyCacheError extends EmptyCacheErrorBase { | ||
| get message(): string; | ||
| } | ||
| /** | ||
| * Tagged error base for {@link EmptyCacheError}. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `EmptyCacheError` appears in public type signatures. | ||
| * Consumers should use {@link EmptyCacheError} directly. | ||
| * | ||
| * @internal | ||
| */ | ||
| export declare const EmptyCacheErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "EmptyCacheError"; | ||
| } & Readonly<A>; | ||
| /** | ||
| * Test whether two {@link SemVer} versions are equal. | ||
| * | ||
| * Uses Effect structural equality ({@link Equal.equals}), which compares | ||
| * `major.minor.patch` and prerelease identifiers but ignores build metadata. | ||
| * | ||
| * For the instance method alternative, use `v.eq(other)`. | ||
| * | ||
| * @see {@link neq} | ||
| * @see {@link compare} | ||
| */ | ||
| export declare const equal: { | ||
| }; | ||
| /** Test whether `self > that`. Dual API. */ | ||
| static gt: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** | ||
| * Test whether two {@link Range}s are semantically equivalent. | ||
| * | ||
| * Two ranges are equivalent when each is a {@link isSubset | subset} of the other, | ||
| * meaning they match exactly the same set of versions. | ||
| * | ||
| * @see {@link isSubset} | ||
| */ | ||
| export declare const equivalent: { | ||
| (b: Range): (a: Range) => boolean; | ||
| (a: Range, b: Range): boolean; | ||
| }; | ||
| /** | ||
| * Filter an array of {@link SemVer} versions to only those satisfying a {@link Range}. | ||
| * | ||
| * For the instance method alternative, use `range.filter(versions)`. | ||
| * | ||
| * @see {@link satisfies} | ||
| */ | ||
| export declare const filter: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => ReadonlyArray<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): ReadonlyArray<SemVer>; | ||
| }; | ||
| /** | ||
| * Test whether `self` is strictly greater than `that`. | ||
| * | ||
| * For the instance method alternative, use `v.gt(other)`. | ||
| * | ||
| * @see {@link gte} | ||
| * @see {@link compare} | ||
| */ | ||
| export declare const gt: { | ||
| }; | ||
| /** Test whether `self >= that`. Dual API. */ | ||
| static gte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| }; | ||
| /** Test whether `self < that`. Dual API. */ | ||
| static lt: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether `self <= that`. Dual API. */ | ||
| static lte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether `self !== that`. Dual API. */ | ||
| static neq: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether two versions are equal (ignores build). Dual API. */ | ||
| static equal: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Compute diff between two versions. Dual API. */ | ||
| static diff: { | ||
| (that: SemVer): (self: SemVer) => VersionDiff; | ||
| (self: SemVer, that: SemVer): VersionDiff; | ||
| }; | ||
| /** Sort versions ascending. */ | ||
| static sort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| /** Sort versions descending. */ | ||
| static rsort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| /** Highest version, or `Option.none()` if empty. */ | ||
| static max: (versions: ReadonlyArray<SemVer>) => import("effect/Option").Option<SemVer>; | ||
| /** Lowest version, or `Option.none()` if empty. */ | ||
| static min: (versions: ReadonlyArray<SemVer>) => import("effect/Option").Option<SemVer>; | ||
| /** Compare `this` to `that` per SemVer 2.0.0 precedence. Returns `-1`, `0`, or `1`. */ | ||
| compare(that: SemVer): -1 | 0 | 1; | ||
| /** Test whether `this > that`. */ | ||
| gt(that: SemVer): boolean; | ||
| /** Test whether `this >= that`. */ | ||
| gte(that: SemVer): boolean; | ||
| /** Test whether `this < that`. */ | ||
| lt(that: SemVer): boolean; | ||
| /** Test whether `this <= that`. */ | ||
| lte(that: SemVer): boolean; | ||
| /** Test whether `this` equals `that` (ignores build metadata). */ | ||
| equal(that: SemVer): boolean; | ||
| /** Alias for {@link equal}. */ | ||
| eq(that: SemVer): boolean; | ||
| /** Test whether `this` does not equal `that` (ignores build metadata). */ | ||
| neq(that: SemVer): boolean; | ||
| /** Whether this is a prerelease version. */ | ||
| get isPrerelease(): boolean; | ||
| /** Whether this is a stable (non-prerelease) version. */ | ||
| get isStable(): boolean; | ||
| private _bump; | ||
| /** Version bumping operations. */ | ||
| get bump(): SemVerBump; | ||
| [Equal.symbol](that: Equal.Equal): boolean; | ||
| [Hash.symbol](): number; | ||
| toString(): string; | ||
| toJSON(): unknown; | ||
| } | ||
| /** | ||
| * Test whether `self` is greater than or equal to `that`. | ||
| * Grouped bump operations returned by {@link SemVer.bump}. | ||
| * | ||
| * For the instance method alternative, use `v.gte(other)`. | ||
| * | ||
| * @see {@link gt} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| export declare const gte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| declare class SemVerBump { | ||
| private readonly v; | ||
| constructor(v: SemVer); | ||
| /** Bump major (resets minor, patch, prerelease, build). */ | ||
| major(): SemVer; | ||
| /** Bump minor (resets patch, prerelease, build). */ | ||
| minor(): SemVer; | ||
| /** Bump patch (resets prerelease, build). */ | ||
| patch(): SemVer; | ||
| /** Bump prerelease, optionally with a named identifier. */ | ||
| prerelease(id?: string): SemVer; | ||
| /** Strip prerelease and build, keeping major.minor.patch. */ | ||
| release(): SemVer; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/InvalidBumpError.d.ts | ||
| /** | ||
| * Compute the intersection of two {@link Range}s using a cross-product of | ||
| * their comparator sets. | ||
| * Tagged error base for {@link InvalidBumpError}. | ||
| * | ||
| * The resulting range matches only versions that satisfy both input ranges. | ||
| * Returns an {@link Effect.Effect} that fails with | ||
| * {@link UnsatisfiableConstraintError} when no satisfiable comparator set | ||
| * remains after intersection. | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `InvalidBumpError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use {@link InvalidBumpError} | ||
| * directly rather than referencing this base. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseRange, intersect } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* parseRange(">=1.0.0"); | ||
| * const b = yield* parseRange("<2.0.0"); | ||
| * const both = yield* intersect(a, b); | ||
| * // both matches >=1.0.0 AND <2.0.0 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link union} | ||
| * @see {@link UnsatisfiableConstraintError} | ||
| * @public | ||
| */ | ||
| export declare const intersect: { | ||
| (b: Range): (a: Range) => Effect_2.Effect<Range, UnsatisfiableConstraintError>; | ||
| (a: Range, b: Range): Effect_2.Effect<Range, UnsatisfiableConstraintError>; | ||
| }; | ||
| declare const InvalidBumpErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "InvalidBumpError"; | ||
| } & Readonly<A>; | ||
| /** | ||
@@ -360,26 +269,26 @@ * Indicates that a version bump operation cannot be applied to the given version. | ||
| * @see {@link bumpRelease} | ||
| * @public | ||
| */ | ||
| export declare class InvalidBumpError extends InvalidBumpErrorBase<{ | ||
| /** The version that the bump was attempted on. */ | ||
| readonly version: SemVer; | ||
| /** The bump type that was requested (e.g., `"major"`, `"prerelease"`). */ | ||
| readonly type: string; | ||
| declare class InvalidBumpError extends InvalidBumpErrorBase<{ | ||
| /** The version that the bump was attempted on. */readonly version: SemVer; /** The bump type that was requested (e.g., `"major"`, `"prerelease"`). */ | ||
| readonly type: string; | ||
| }> { | ||
| get message(): string; | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/InvalidComparatorError.d.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidBumpError}. | ||
| * Tagged error base for {@link InvalidComparatorError}. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `InvalidBumpError` appears in public type signatures. | ||
| * Consumers should use {@link InvalidBumpError} directly. | ||
| * Marked `@public` (rather than `@internal`) because `InvalidComparatorError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link InvalidComparatorError} directly rather than referencing this base. | ||
| * | ||
| * @internal | ||
| * @public | ||
| */ | ||
| export declare const InvalidBumpErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "InvalidBumpError"; | ||
| declare const InvalidComparatorErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "InvalidComparatorError"; | ||
| } & Readonly<A>; | ||
| /** | ||
@@ -393,26 +302,26 @@ * Indicates that a string could not be parsed as a valid single {@link Comparator}. | ||
| * @see {@link Comparator} | ||
| * @public | ||
| */ | ||
| export declare class InvalidComparatorError extends InvalidComparatorErrorBase<{ | ||
| /** The raw input string that failed to parse. */ | ||
| readonly input: string; | ||
| /** The character position where parsing failed, if available. */ | ||
| readonly position?: number; | ||
| declare class InvalidComparatorError extends InvalidComparatorErrorBase<{ | ||
| /** The raw input string that failed to parse. */readonly input: string; /** The character position where parsing failed, if available. */ | ||
| readonly position?: number; | ||
| }> { | ||
| get message(): string; | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/InvalidPrereleaseError.d.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidComparatorError}. | ||
| * Tagged error base for {@link InvalidPrereleaseError}. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `InvalidComparatorError` appears in public type signatures. | ||
| * Consumers should use {@link InvalidComparatorError} directly. | ||
| * Marked `@public` (rather than `@internal`) because `InvalidPrereleaseError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link InvalidPrereleaseError} directly rather than referencing this base. | ||
| * | ||
| * @internal | ||
| * @public | ||
| */ | ||
| export declare const InvalidComparatorErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "InvalidComparatorError"; | ||
| declare const InvalidPrereleaseErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "InvalidPrereleaseError"; | ||
| } & Readonly<A>; | ||
| /** | ||
@@ -426,24 +335,25 @@ * Indicates that a prerelease identifier is not valid per SemVer 2.0.0. | ||
| * @see {@link https://semver.org/#spec-item-9 | SemVer 2.0.0 Section 9} | ||
| * @public | ||
| */ | ||
| export declare class InvalidPrereleaseError extends InvalidPrereleaseErrorBase<{ | ||
| /** The invalid prerelease identifier string. */ | ||
| readonly input: string; | ||
| declare class InvalidPrereleaseError extends InvalidPrereleaseErrorBase<{ | ||
| /** The invalid prerelease identifier string. */readonly input: string; | ||
| }> { | ||
| get message(): string; | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/InvalidRangeError.d.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidPrereleaseError}. | ||
| * Tagged error base for {@link InvalidRangeError}. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `InvalidPrereleaseError` appears in public type signatures. | ||
| * Consumers should use {@link InvalidPrereleaseError} directly. | ||
| * Marked `@public` (rather than `@internal`) because `InvalidRangeError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use {@link InvalidRangeError} | ||
| * directly rather than referencing this base. | ||
| * | ||
| * @internal | ||
| * @public | ||
| */ | ||
| export declare const InvalidPrereleaseErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "InvalidPrereleaseError"; | ||
| declare const InvalidRangeErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "InvalidRangeError"; | ||
| } & Readonly<A>; | ||
| /** | ||
@@ -459,392 +369,518 @@ * Indicates that a string could not be parsed as a valid SemVer range expression. | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| export declare class InvalidRangeError extends InvalidRangeErrorBase<{ | ||
| /** The raw input string that failed to parse. */ | ||
| readonly input: string; | ||
| /** The character position where parsing failed, if available. */ | ||
| readonly position?: number; | ||
| declare class InvalidRangeError extends InvalidRangeErrorBase<{ | ||
| /** The raw input string that failed to parse. */readonly input: string; /** The character position where parsing failed, if available. */ | ||
| readonly position?: number; | ||
| }> { | ||
| get message(): string; | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/schemas/Comparator.d.ts | ||
| declare const Comparator_base: Schema.TaggedClass<Comparator, "Comparator", { | ||
| readonly _tag: Schema.tag<"Comparator">; | ||
| } & { | ||
| operator: Schema.Literal<["=", ">", ">=", "<", "<="]>; | ||
| version: typeof SemVer; | ||
| }>; | ||
| /** | ||
| * Tagged error base for {@link InvalidRangeError}. | ||
| * A single version constraint consisting of a comparison operator and a version. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `InvalidRangeError` appears in public type signatures. | ||
| * Consumers should use {@link InvalidRangeError} directly. | ||
| * @example | ||
| * ```typescript | ||
| * import { Comparator } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * @internal | ||
| * const program = Effect.gen(function* () { | ||
| * const comp = yield* Comparator.parse(">=1.2.3"); | ||
| * console.log(comp.operator); // ">=" | ||
| * console.log(comp.version.toString()); // "1.2.3" | ||
| * console.log(comp.test(new SemVer({ major: 2, minor: 0, patch: 0, prerelease: [], build: [] }))); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Range} | ||
| * @see {@link SemVer} | ||
| * @public | ||
| */ | ||
| export declare const InvalidRangeErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "InvalidRangeError"; | ||
| } & Readonly<A>; | ||
| declare class Comparator extends Comparator_base { | ||
| /** Parse a comparator string (e.g. `">=1.2.3"`). Wired at module load by index.ts. */ | ||
| static parse: (input: string) => import("effect/Effect").Effect<Comparator, InvalidComparatorError>; | ||
| /** Test whether a version satisfies this comparator. */ | ||
| test(version: SemVer): boolean; | ||
| toString(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/schemas/Range.d.ts | ||
| /** | ||
| * Indicates that a string could not be parsed as a valid SemVer 2.0.0 version. | ||
| * A comparator set: an array of {@link Comparator} instances combined with AND | ||
| * semantics. A version must satisfy every comparator in the set to match. | ||
| * | ||
| * This error is returned when {@link parseValidSemVer} (or the `SemVerParser` service) | ||
| * encounters input that does not conform to the `major.minor.patch[-prerelease][+build]` | ||
| * format. Unlike node-semver, no loose parsing or `v`-prefix coercion is performed. | ||
| * @see {@link Range} | ||
| * @public | ||
| */ | ||
| type ComparatorSet = ReadonlyArray<Comparator>; | ||
| declare const Range_base: Schema.TaggedClass<Range, "Range", { | ||
| readonly _tag: Schema.tag<"Range">; | ||
| } & { | ||
| sets: Schema.Array$<Schema.Array$<typeof Comparator>>; | ||
| }>; | ||
| /** | ||
| * A SemVer range expression, represented as a union (OR) of {@link ComparatorSet}s. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { Range, SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const range = yield* Range.parse("^1.0.0"); | ||
| * const v = new SemVer({ major: 1, minor: 5, patch: 0, prerelease: [], build: [] }); | ||
| * console.log(range.test(v)); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Comparator} | ||
| * @see {@link ComparatorSet} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| export declare class InvalidVersionError extends InvalidVersionErrorBase<{ | ||
| /** The raw input string that failed to parse. */ | ||
| readonly input: string; | ||
| /** The character position where parsing failed, if available. */ | ||
| readonly position?: number; | ||
| }> { | ||
| get message(): string; | ||
| declare class Range extends Range_base { | ||
| /** Parse a SemVer range expression. Wired at module load by index.ts. */ | ||
| static parse: (input: string) => import("effect/Effect").Effect<Range, InvalidRangeError>; | ||
| /** Test whether a version satisfies a range. Dual API. Wired at module load. */ | ||
| static satisfies: { | ||
| (range: Range): (version: SemVer) => boolean; | ||
| (version: SemVer, range: Range): boolean; | ||
| }; | ||
| /** Filter versions that satisfy a range. Dual API. Wired at module load. */ | ||
| static filter: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => ReadonlyArray<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): ReadonlyArray<SemVer>; | ||
| }; | ||
| /** Highest satisfying version. Dual API. Wired at module load. */ | ||
| static maxSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => import("effect/Option").Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): import("effect/Option").Option<SemVer>; | ||
| }; | ||
| /** Lowest satisfying version. Dual API. Wired at module load. */ | ||
| static minSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => import("effect/Option").Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): import("effect/Option").Option<SemVer>; | ||
| }; | ||
| /** Test whether a version satisfies this range. */ | ||
| test(version: SemVer): boolean; | ||
| /** Filter versions that satisfy this range, preserving order. */ | ||
| filter(versions: ReadonlyArray<SemVer>): ReadonlyArray<SemVer>; | ||
| toString(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/UnsatisfiableConstraintError.d.ts | ||
| /** | ||
| * Tagged error base for {@link InvalidVersionError}. | ||
| * Tagged error base for {@link UnsatisfiableConstraintError}. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `InvalidVersionError` appears in public type signatures. | ||
| * Consumers should use {@link InvalidVersionError} directly. | ||
| * Marked `@public` (rather than `@internal`) because `UnsatisfiableConstraintError` | ||
| * extends it directly: API Extractor requires a class's release tag to be at least | ||
| * as public as every type in its `extends` clause. Consumers should use | ||
| * {@link UnsatisfiableConstraintError} directly rather than referencing this base. | ||
| * | ||
| * @internal | ||
| * @public | ||
| */ | ||
| export declare const InvalidVersionErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "InvalidVersionError"; | ||
| declare const UnsatisfiableConstraintErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "UnsatisfiableConstraintError"; | ||
| } & Readonly<A>; | ||
| /** | ||
| * Test whether a version has prerelease identifiers. | ||
| * Indicates that the intersection of two or more {@link Range}s produces an | ||
| * empty (unsatisfiable) result. | ||
| * | ||
| * For the instance getter alternative, use `v.isPrerelease`. | ||
| * Returned by {@link intersect} when no comparator set in the cross-product | ||
| * of the input ranges is satisfiable. | ||
| * | ||
| * @see {@link isStable} | ||
| * @see {@link intersect} | ||
| * @see {@link Range} | ||
| * @public | ||
| */ | ||
| export declare const isPrerelease: (v: SemVer) => boolean; | ||
| declare class UnsatisfiableConstraintError extends UnsatisfiableConstraintErrorBase<{ | ||
| /** The ranges that were being intersected. */readonly constraints: ReadonlyArray<Range>; | ||
| }> { | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/UnsatisfiedRangeError.d.ts | ||
| /** | ||
| * Test whether a version is a stable release (no prerelease identifiers). | ||
| * Tagged error base for {@link UnsatisfiedRangeError}. | ||
| * | ||
| * For the instance getter alternative, use `v.isStable`. | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `UnsatisfiedRangeError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link UnsatisfiedRangeError} directly rather than referencing this base. | ||
| * | ||
| * @see {@link isPrerelease} | ||
| * @public | ||
| */ | ||
| export declare const isStable: (v: SemVer) => boolean; | ||
| declare const UnsatisfiedRangeErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "UnsatisfiedRangeError"; | ||
| } & Readonly<A>; | ||
| /** | ||
| * Check whether every version matched by `sub` is also matched by `sup`. | ||
| * Indicates that no version in a given set satisfies a {@link Range}. | ||
| * | ||
| * Returns `true` if the `sub` range is a subset of (i.e., fully contained by) | ||
| * the `sup` range. This is determined by checking that every comparator set in | ||
| * `sub` is implied by at least one comparator set in `sup`. | ||
| * Returned by `VersionCache.resolve` and `VersionCache.resolveString` | ||
| * when the cache contains versions but none match the requested range. | ||
| * | ||
| * @remarks | ||
| * This check is a conservative approximation: it may return `false` for ranges | ||
| * that are technically subsets when the sub-range straddles comparator-set | ||
| * boundaries in the sup-range. For example, `>=1.0.0 <3.0.0` is a subset of | ||
| * `>=1.0.0 <2.0.0 || >=2.0.0 <3.0.0`, but `isSubset` returns `false` because | ||
| * no single sup-set fully implies the sub-set. This is a known limitation; | ||
| * false negatives are safe (they prevent incorrect simplification). | ||
| * | ||
| * @see {@link equivalent} | ||
| * @see {@link intersect} | ||
| * @see {@link VersionCache} | ||
| * @see {@link Range} | ||
| * @public | ||
| */ | ||
| export declare const isSubset: { | ||
| (sup: Range): (sub: Range) => boolean; | ||
| (sub: Range, sup: Range): boolean; | ||
| }; | ||
| declare class UnsatisfiedRangeError extends UnsatisfiedRangeErrorBase<{ | ||
| /** The range that could not be satisfied. */readonly range: Range; /** The versions that were available for matching. */ | ||
| readonly available: ReadonlyArray<SemVer>; | ||
| }> { | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/errors/VersionFetchError.d.ts | ||
| /** | ||
| * Test whether `self` is strictly less than `that`. | ||
| * Tagged error base for {@link VersionFetchError}. | ||
| * | ||
| * For the instance method alternative, use `v.lt(other)`. | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `VersionFetchError` extends it | ||
| * directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link VersionFetchError} directly rather than referencing this base. | ||
| * | ||
| * @see {@link lte} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| export declare const lt: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| declare const VersionFetchErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "VersionFetchError"; | ||
| } & Readonly<A>; | ||
| /** | ||
| * Test whether `self` is less than or equal to `that`. | ||
| * Indicates that fetching versions from an external source failed. | ||
| * | ||
| * For the instance method alternative, use `v.lte(other)`. | ||
| * Returned by {@link VersionFetcher} implementations when a network request or | ||
| * registry lookup fails. The `source` field identifies the registry or endpoint, | ||
| * and `cause` may contain the underlying error. | ||
| * | ||
| * @see {@link lt} | ||
| * @see {@link compare} | ||
| * @see {@link VersionFetcher} | ||
| * @public | ||
| */ | ||
| export declare const lte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| declare class VersionFetchError extends VersionFetchErrorBase<{ | ||
| /** The source identifier (e.g., registry URL or package name). */readonly source: string; /** A human-readable description of the failure. */ | ||
| readonly message: string; /** The underlying error, if any. */ | ||
| readonly cause?: unknown; | ||
| }> {} | ||
| //#endregion | ||
| //#region src/errors/VersionNotFoundError.d.ts | ||
| /** | ||
| * Find the highest version in an array. | ||
| * Tagged error base for {@link VersionNotFoundError}. | ||
| * | ||
| * Returns `Option.none()` for an empty array. | ||
| * @privateRemarks | ||
| * Marked `@public` (rather than `@internal`) because `VersionNotFoundError` extends | ||
| * it directly: API Extractor requires a class's release tag to be at least as public | ||
| * as every type in its `extends` clause. Consumers should use | ||
| * {@link VersionNotFoundError} directly rather than referencing this base. | ||
| * | ||
| * @see {@link min} | ||
| * @see {@link sort} | ||
| * @public | ||
| */ | ||
| export declare const max: (versions: ReadonlyArray<SemVer>) => Option_2.Option<SemVer>; | ||
| declare const VersionNotFoundErrorBase: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & { | ||
| readonly _tag: "VersionNotFoundError"; | ||
| } & Readonly<A>; | ||
| /** | ||
| * Find the highest {@link SemVer} version satisfying a {@link Range}. | ||
| * Indicates that a specific version was not found in the {@link VersionCache}. | ||
| * | ||
| * Returns `Option.none()` if no version satisfies the range. | ||
| * Returned by navigation operations (`diff`, `next`, `prev`) when the referenced | ||
| * version has not been loaded into the cache. | ||
| * | ||
| * @see {@link minSatisfying} | ||
| * @see {@link satisfies} | ||
| * @see {@link VersionCache} | ||
| * @public | ||
| */ | ||
| export declare const maxSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => Option_2.Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): Option_2.Option<SemVer>; | ||
| }; | ||
| declare class VersionNotFoundError extends VersionNotFoundErrorBase<{ | ||
| /** The version that was not found in the cache. */readonly version: SemVer; | ||
| }> { | ||
| get message(): string; | ||
| } | ||
| //#endregion | ||
| //#region src/services/SemVerParser.d.ts | ||
| declare const SemVerParser_base: Context.TagClass<SemVerParser, "semver-effect/SemVerParser", { | ||
| /** Parse a string into a {@link SemVer}. Fails with {@link InvalidVersionError} on invalid input. */readonly parseVersion: (input: string) => Effect.Effect<SemVer, InvalidVersionError>; /** Parse a range expression into a {@link Range}. Fails with {@link InvalidRangeError} on invalid input. */ | ||
| readonly parseRange: (input: string) => Effect.Effect<Range, InvalidRangeError>; /** Parse a single comparator string into a {@link Comparator}. Fails with {@link InvalidComparatorError} on invalid input. */ | ||
| readonly parseComparator: (input: string) => Effect.Effect<Comparator, InvalidComparatorError>; | ||
| }>; | ||
| /** | ||
| * Find the lowest version in an array. | ||
| * Effect service interface for parsing SemVer 2.0.0 strings. | ||
| * | ||
| * Returns `Option.none()` for an empty array. | ||
| * Provides effectful parsing of version strings, range expressions, and | ||
| * individual comparators. All parsing is strict SemVer 2.0.0; no loose mode | ||
| * or `v`-prefix coercion is supported. | ||
| * | ||
| * @see {@link max} | ||
| * @see {@link sort} | ||
| */ | ||
| export declare const min: (versions: ReadonlyArray<SemVer>) => Option_2.Option<SemVer>; | ||
| /** | ||
| * Find the lowest {@link SemVer} version satisfying a {@link Range}. | ||
| * Use {@link SemVerParserLive} to provide a concrete implementation. | ||
| * | ||
| * Returns `Option.none()` if no version satisfies the range. | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVerParser, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * @see {@link maxSatisfying} | ||
| * @see {@link satisfies} | ||
| */ | ||
| export declare const minSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => Option_2.Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): Option_2.Option<SemVer>; | ||
| }; | ||
| /** | ||
| * Test whether two {@link SemVer} versions are not equal. | ||
| * const program = Effect.gen(function* () { | ||
| * const parser = yield* SemVerParser; | ||
| * const version = yield* parser.parseVersion("1.2.3"); | ||
| * const range = yield* parser.parseRange("^1.0.0"); | ||
| * const comp = yield* parser.parseComparator(">=2.0.0"); | ||
| * }).pipe(Effect.provide(SemVerParserLive)); | ||
| * ``` | ||
| * | ||
| * For the instance method alternative, use `v.neq(other)`. | ||
| * | ||
| * @see {@link equal} | ||
| * @see {@link SemVerParserLive} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @public | ||
| */ | ||
| export declare const neq: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| declare class SemVerParser extends SemVerParser_base {} | ||
| //#endregion | ||
| //#region src/layers/SemVerParserLive.d.ts | ||
| /** | ||
| * Parse a SemVer range expression string and normalize the result. | ||
| * Live Effect `Layer` that provides the {@link SemVerParser} service. | ||
| * | ||
| * This is a convenience function that performs the same parsing and normalization | ||
| * as {@link SemVerParser.parseRange} but without requiring the service in scope. | ||
| * Returns an {@link Effect.Effect} that fails with {@link InvalidRangeError} | ||
| * on invalid input. | ||
| * This layer has no dependencies and can be provided directly. It delegates to | ||
| * the strict SemVer 2.0.0 recursive-descent parser implemented in the grammar | ||
| * module, with range normalization applied to parsed range expressions. | ||
| * | ||
| * Supports hyphen ranges (`1.0.0 - 2.0.0`), X-ranges (`1.x`, `*`), tilde | ||
| * ranges (`~1.2.3`), caret ranges (`^1.2.3`), and `||`-separated unions. | ||
| * All version components must be strictly valid SemVer 2.0.0 (no loose parsing). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseRange, parseValidSemVer, satisfies } from "semver-effect"; | ||
| * import { SemVerParser, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const range = yield* parseRange("^1.0.0"); | ||
| * const v = yield* parseValidSemVer("1.5.0"); | ||
| * console.log(satisfies(v, range)); // true | ||
| * }); | ||
| * const parser = yield* SemVerParser; | ||
| * const v = yield* parser.parseVersion("1.0.0"); | ||
| * }).pipe(Effect.provide(SemVerParserLive)); | ||
| * ``` | ||
| * | ||
| * @see {@link Range} | ||
| * @see {@link SemVerParser} | ||
| * @see {@link InvalidRangeError} | ||
| * @see {@link https://effect.website/docs/context-management/layers | Effect Layers} | ||
| * @public | ||
| */ | ||
| export declare const parseRange: (input: string) => Effect_2.Effect<Range, InvalidRangeError>; | ||
| declare const SemVerParserLive: Layer.Layer<SemVerParser>; | ||
| //#endregion | ||
| //#region src/services/VersionCache.d.ts | ||
| declare const VersionCache_base: Context.TagClass<VersionCache, "semver-effect/VersionCache", { | ||
| /** Replace all cached versions with the given array. */readonly load: (versions: ReadonlyArray<SemVer>) => Effect.Effect<void, never>; /** Add a single version to the cache. */ | ||
| readonly add: (version: SemVer) => Effect.Effect<void, never>; /** Remove a single version from the cache. */ | ||
| readonly remove: (version: SemVer) => Effect.Effect<void, never>; /** Retrieve all cached versions in sorted order. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly versions: Effect.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; /** Retrieve the highest version in the cache. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly latest: () => Effect.Effect<SemVer, EmptyCacheError>; /** Retrieve the lowest version in the cache. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly oldest: () => Effect.Effect<SemVer, EmptyCacheError>; /** Find the highest version satisfying a {@link Range}. Fails with {@link UnsatisfiedRangeError} if none match. */ | ||
| readonly resolve: (range: Range) => Effect.Effect<SemVer, UnsatisfiedRangeError>; /** Parse a range string and resolve it. Fails with {@link InvalidRangeError} or {@link UnsatisfiedRangeError}. */ | ||
| readonly resolveString: (input: string) => Effect.Effect<SemVer, InvalidRangeError | UnsatisfiedRangeError>; | ||
| /** | ||
| * Return all cached versions satisfying a {@link Range}. | ||
| * Fails with {@link EmptyCacheError} if the cache is empty (nothing loaded). | ||
| * Returns an empty array if the cache is non-empty but no versions match. | ||
| */ | ||
| readonly filter: (range: Range) => Effect.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; /** Group cached versions by major, minor, or patch level. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly groupBy: (strategy: "major" | "minor" | "patch") => Effect.Effect<Map<string, ReadonlyArray<SemVer>>, EmptyCacheError>; /** Return the latest version for each distinct major version. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly latestByMajor: () => Effect.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; /** Return the latest version for each distinct major.minor pair. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly latestByMinor: () => Effect.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; /** Compute a {@link VersionDiff} between two versions. Fails with {@link VersionNotFoundError} if either is missing. */ | ||
| readonly diff: (a: SemVer, b: SemVer) => Effect.Effect<VersionDiff, VersionNotFoundError>; /** Get the next higher version after the given one. Fails with {@link VersionNotFoundError} if not in cache. */ | ||
| readonly next: (version: SemVer) => Effect.Effect<Option.Option<SemVer>, VersionNotFoundError>; /** Get the next lower version before the given one. Fails with {@link VersionNotFoundError} if not in cache. */ | ||
| readonly prev: (version: SemVer) => Effect.Effect<Option.Option<SemVer>, VersionNotFoundError>; | ||
| }>; | ||
| /** | ||
| * Parse a string into a single {@link Comparator}. | ||
| * Effect service interface for an in-memory sorted version cache. | ||
| * | ||
| * Accepts an optional operator prefix (`=`, `>`, `>=`, `<`, `<=`) followed by | ||
| * a complete `major.minor.patch[-prerelease][+build]` version string. Wildcards | ||
| * and range syntax (tilde, caret, hyphen, X-range) are not allowed. | ||
| * Provides mutation, query, resolution, grouping, and navigation operations | ||
| * over a set of {@link SemVer} versions. Versions are maintained in sorted | ||
| * order using {@link SemVerOrder}. | ||
| * | ||
| * If no operator is specified, `"="` (exact match) is assumed. | ||
| * Use {@link VersionCacheLive} to provide a concrete implementation backed by | ||
| * an Effect `Ref` of a `SortedSet`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseSingleComparator } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * import { SemVer, VersionCache, VersionCacheLive, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const comp = yield* parseSingleComparator(">=1.0.0"); | ||
| * console.log(comp.operator); // ">=" | ||
| * console.log(comp.version.toString()); // "1.0.0" | ||
| * }); | ||
| * const cache = yield* VersionCache; | ||
| * const v1 = new SemVer({ major: 1, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * const v2 = new SemVer({ major: 2, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * yield* cache.load([v1, v2]); | ||
| * const latest = yield* cache.latest(); | ||
| * console.log(latest.toString()); // "2.0.0" | ||
| * }).pipe(Effect.provide(Layer.merge(VersionCacheLive, SemVerParserLive))); | ||
| * ``` | ||
| * | ||
| * @see {@link Comparator} | ||
| * @see {@link InvalidComparatorError} | ||
| * @see {@link VersionCacheLive} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @public | ||
| */ | ||
| export declare const parseSingleComparator: (raw: string) => Effect_2.Effect<Comparator, InvalidComparatorError>; | ||
| declare class VersionCache extends VersionCache_base {} | ||
| //#endregion | ||
| //#region src/layers/VersionCacheLive.d.ts | ||
| /** | ||
| * Parse a string into a {@link SemVer} using a strict SemVer 2.0.0 | ||
| * recursive-descent parser. | ||
| * Live Effect `Layer` that provides the {@link VersionCache} service. | ||
| * | ||
| * Rejects `v`/`V` prefixes, `=` prefixes, leading zeros on numeric identifiers, | ||
| * and any input that does not fully consume as a valid `major.minor.patch[-prerelease][+build]` | ||
| * string. Returns an {@link Effect.Effect} that fails with {@link InvalidVersionError} | ||
| * on invalid input. | ||
| * Backed by an Effect `Ref` containing a `SortedSet` ordered by {@link SemVerOrder}. | ||
| * Requires a {@link SemVerParser} in its dependency graph (used by `resolveString` | ||
| * to parse range strings). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseValidSemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * import { SemVer, VersionCache, VersionCacheLive, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * | ||
| * const AppLayer = Layer.merge(VersionCacheLive, SemVerParserLive); | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* parseValidSemVer("1.2.3-beta.1+build.42"); | ||
| * console.log(v.toString()); // "1.2.3-beta.1+build.42" | ||
| * }); | ||
| * const cache = yield* VersionCache; | ||
| * const v = new SemVer({ major: 1, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * yield* cache.add(v); | ||
| * const latest = yield* cache.latest(); | ||
| * }).pipe(Effect.provide(AppLayer)); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVer} | ||
| * @see {@link InvalidVersionError} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @see {@link VersionCache} | ||
| * @see {@link SemVerParserLive} | ||
| * @see {@link https://effect.website/docs/context-management/layers | Effect Layers} | ||
| * @public | ||
| */ | ||
| export declare const parseValidSemVer: (raw: string) => Effect_2.Effect<SemVer, InvalidVersionError>; | ||
| declare const VersionCacheLive: Layer.Layer<VersionCache, never, SemVerParser>; | ||
| //#endregion | ||
| //#region src/services/VersionFetcher.d.ts | ||
| declare const VersionFetcher_base: Context.TagClass<VersionFetcher, "semver-effect/VersionFetcher", { | ||
| /** Fetch all available versions for the given package name. Fails with {@link VersionFetchError} on failure. */readonly fetch: (packageName: string) => Effect.Effect<ReadonlyArray<SemVer>, VersionFetchError>; | ||
| }>; | ||
| /** | ||
| * Convert any {@link Printable} schema value to its human-readable string form. | ||
| * Effect service interface for fetching available versions from an external source. | ||
| * | ||
| * Dispatches on the `_tag` field to call the appropriate `toString()` method. | ||
| * This is a convenience for logging and display; each schema type also has its | ||
| * own `toString()` method. | ||
| * Implementations might query a package registry (e.g., npm, GitHub Releases) and | ||
| * return parsed {@link SemVer} instances. This is an interface-only service; no | ||
| * default live layer is provided by this library -- consumers must supply their own. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { prettyPrint, parseValidSemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * import type { VersionFetcher } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* parseValidSemVer("1.2.3-alpha.1"); | ||
| * console.log(prettyPrint(v)); // "1.2.3-alpha.1" | ||
| * // Example: a stub implementation | ||
| * const StubFetcher = Layer.succeed(VersionFetcher, { | ||
| * fetch: (_packageName) => Effect.succeed([]), | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Printable} | ||
| * @see {@link VersionFetchError} | ||
| * @see {@link VersionCache} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @public | ||
| */ | ||
| export declare const prettyPrint: (value: Printable) => string; | ||
| declare class VersionFetcher extends VersionFetcher_base {} | ||
| //#endregion | ||
| //#region src/utils/algebra.d.ts | ||
| /** | ||
| * Union of all schema types that can be pretty-printed by {@link prettyPrint}. | ||
| * Combine two {@link Range}s with OR semantics (union of comparator sets). | ||
| * | ||
| * @see {@link prettyPrint} | ||
| */ | ||
| export declare type Printable = SemVer | Comparator | Range | VersionDiff; | ||
| /** | ||
| * A SemVer range expression, represented as a union (OR) of {@link ComparatorSet}s. | ||
| * The resulting range matches any version that satisfies either input range. | ||
| * Internally, this concatenates the comparator sets from both ranges. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { Range, SemVer } from "semver-effect"; | ||
| * import { parseRange, union } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const range = yield* Range.parse("^1.0.0"); | ||
| * const v = new SemVer({ major: 1, minor: 5, patch: 0, prerelease: [], build: [] }); | ||
| * console.log(range.test(v)); // true | ||
| * const a = yield* parseRange("^1.0.0"); | ||
| * const b = yield* parseRange("^2.0.0"); | ||
| * const combined = union(a, b); | ||
| * // combined matches versions satisfying ^1.0.0 OR ^2.0.0 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Comparator} | ||
| * @see {@link ComparatorSet} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @see {@link intersect} | ||
| * @see {@link equivalent} | ||
| * @public | ||
| */ | ||
| export declare class Range extends Range_base { | ||
| /** Parse a SemVer range expression. Wired at module load by index.ts. */ | ||
| static parse: (input: string) => Effect<Range, InvalidRangeError>; | ||
| /** Test whether a version satisfies a range. Dual API. Wired at module load. */ | ||
| static satisfies: { | ||
| (range: Range): (version: SemVer) => boolean; | ||
| (version: SemVer, range: Range): boolean; | ||
| }; | ||
| /** Filter versions that satisfy a range. Dual API. Wired at module load. */ | ||
| static filter: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => ReadonlyArray<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): ReadonlyArray<SemVer>; | ||
| }; | ||
| /** Highest satisfying version. Dual API. Wired at module load. */ | ||
| static maxSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): Option<SemVer>; | ||
| }; | ||
| /** Lowest satisfying version. Dual API. Wired at module load. */ | ||
| static minSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): Option<SemVer>; | ||
| }; | ||
| /** Test whether a version satisfies this range. */ | ||
| test(version: SemVer): boolean; | ||
| /** Filter versions that satisfy this range, preserving order. */ | ||
| filter(versions: ReadonlyArray<SemVer>): ReadonlyArray<SemVer>; | ||
| toString(): string; | ||
| } | ||
| declare const Range_base: Schema.TaggedClass<Range, "Range", { | ||
| readonly _tag: Schema.tag<"Range">; | ||
| } & { | ||
| sets: Schema.Array$<Schema.Array$<typeof Comparator>>; | ||
| }>; | ||
| declare const union: { | ||
| (b: Range): (a: Range) => Range; | ||
| (a: Range, b: Range): Range; | ||
| }; | ||
| /** | ||
| * Sort an array of {@link SemVer} versions in descending order. | ||
| * Compute the intersection of two {@link Range}s using a cross-product of | ||
| * their comparator sets. | ||
| * | ||
| * Returns a new array; the input is not mutated. | ||
| * The resulting range matches only versions that satisfy both input ranges. | ||
| * Returns an `Effect.Effect` that fails with | ||
| * {@link UnsatisfiableConstraintError} when no satisfiable comparator set | ||
| * remains after intersection. | ||
| * | ||
| * @see {@link sort} | ||
| * @see {@link SemVerOrder} | ||
| */ | ||
| export declare const rsort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| /** | ||
| * Test whether a {@link SemVer} version satisfies a {@link Range}. | ||
| * | ||
| * For the instance method alternative, use `range.test(version)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer, Range } from "semver-effect"; | ||
| * import { parseRange, intersect } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.5.0"); | ||
| * const r = yield* Range.parse("^1.0.0"); | ||
| * console.log(r.test(v)); // true (instance method) | ||
| * console.log(satisfies(v, r)); // true (standalone function) | ||
| * const a = yield* parseRange(">=1.0.0"); | ||
| * const b = yield* parseRange("<2.0.0"); | ||
| * const both = yield* intersect(a, b); | ||
| * // both matches >=1.0.0 AND <2.0.0 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link filter} | ||
| * @see {@link maxSatisfying} | ||
| * @see {@link minSatisfying} | ||
| * @see {@link union} | ||
| * @see {@link UnsatisfiableConstraintError} | ||
| * @public | ||
| */ | ||
| export declare const satisfies: { | ||
| (range: Range): (version: SemVer) => boolean; | ||
| (version: SemVer, range: Range): boolean; | ||
| declare const intersect: { | ||
| (b: Range): (a: Range) => Effect.Effect<Range, UnsatisfiableConstraintError>; | ||
| (a: Range, b: Range): Effect.Effect<Range, UnsatisfiableConstraintError>; | ||
| }; | ||
| /** | ||
| * A parsed SemVer 2.0.0 version, represented as an Effect `Schema.TaggedClass`. | ||
| * Check whether every version matched by `sub` is also matched by `sup`. | ||
| * | ||
| * Provides instance methods for comparison, bumping, and predicates, plus | ||
| * static methods for parsing and collection operations. | ||
| * Returns `true` if the `sub` range is a subset of (i.e., fully contained by) | ||
| * the `sup` range. This is determined by checking that every comparator set in | ||
| * `sub` is implied by at least one comparator set in `sup`. | ||
| * | ||
| * @remarks | ||
| * This check is a conservative approximation: it may return `false` for ranges | ||
| * that are technically subsets when the sub-range straddles comparator-set | ||
| * boundaries in the sup-range. For example, `>=1.0.0 <3.0.0` is a subset of | ||
| * `>=1.0.0 <2.0.0 || >=2.0.0 <3.0.0`, but `isSubset` returns `false` because | ||
| * no single sup-set fully implies the sub-set. This is a known limitation; | ||
| * false negatives are safe (they prevent incorrect simplification). | ||
| * | ||
| * @see {@link equivalent} | ||
| * @see {@link intersect} | ||
| * @public | ||
| */ | ||
| declare const isSubset: { | ||
| (sup: Range): (sub: Range) => boolean; | ||
| (sub: Range, sup: Range): boolean; | ||
| }; | ||
| /** | ||
| * Test whether two {@link Range}s are semantically equivalent. | ||
| * | ||
| * Two ranges are equivalent when each is a {@link isSubset | subset} of the other, | ||
| * meaning they match exactly the same set of versions. | ||
| * | ||
| * @see {@link isSubset} | ||
| * @public | ||
| */ | ||
| declare const equivalent: { | ||
| (b: Range): (a: Range) => boolean; | ||
| (a: Range, b: Range): boolean; | ||
| }; | ||
| /** | ||
| * Remove redundant comparator sets from a {@link Range}. | ||
| * | ||
| * A comparator set is considered redundant if another set in the range is a | ||
| * subset of it (i.e., the other set is more restrictive and already covered). | ||
| * Returns a new range with only the non-redundant sets. | ||
| * | ||
| * @see {@link isSubset} | ||
| * @see {@link equivalent} | ||
| * @public | ||
| */ | ||
| declare const simplify: (range: Range) => Range; | ||
| //#endregion | ||
| //#region src/utils/bump.d.ts | ||
| /** | ||
| * Increment the major version and reset minor, patch, and prerelease to zero/empty. | ||
| * | ||
| * @example | ||
@@ -857,218 +893,197 @@ * ```typescript | ||
| * const v = yield* SemVer.parse("1.2.3"); | ||
| * const next = v.bump.minor(); // 1.3.0 | ||
| * console.log(v.gt(next)); // false | ||
| * console.log(next.isStable); // true | ||
| * console.log(v.bump.major().toString()); // "2.0.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @see {@link bumpMinor} | ||
| * @see {@link bumpPatch} | ||
| * @public | ||
| */ | ||
| export declare class SemVer extends SemVer_base { | ||
| /** Parse a strict SemVer 2.0.0 string. @remarks Import from `"semver-effect"`, not the schema subpath. */ | ||
| static parse: (input: string) => Effect<SemVer, InvalidVersionError>; | ||
| /** Convenience positional constructor. Wired at module load by index.ts. */ | ||
| static of: (major: number, minor: number, patch: number, prerelease?: ReadonlyArray<string | number>, build?: ReadonlyArray<string>) => SemVer; | ||
| /** Compare two versions. Returns `-1`, `0`, or `1`. Dual API. */ | ||
| static compare: { | ||
| (that: SemVer): (self: SemVer) => -1 | 0 | 1; | ||
| (self: SemVer, that: SemVer): -1 | 0 | 1; | ||
| }; | ||
| /** Test whether `self > that`. Dual API. */ | ||
| static gt: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether `self >= that`. Dual API. */ | ||
| static gte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether `self < that`. Dual API. */ | ||
| static lt: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether `self <= that`. Dual API. */ | ||
| static lte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether `self !== that`. Dual API. */ | ||
| static neq: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Test whether two versions are equal (ignores build). Dual API. */ | ||
| static equal: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** Compute diff between two versions. Dual API. */ | ||
| static diff: { | ||
| (that: SemVer): (self: SemVer) => VersionDiff; | ||
| (self: SemVer, that: SemVer): VersionDiff; | ||
| }; | ||
| /** Sort versions ascending. */ | ||
| static sort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| /** Sort versions descending. */ | ||
| static rsort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| /** Highest version, or `Option.none()` if empty. */ | ||
| static max: (versions: ReadonlyArray<SemVer>) => Option<SemVer>; | ||
| /** Lowest version, or `Option.none()` if empty. */ | ||
| static min: (versions: ReadonlyArray<SemVer>) => Option<SemVer>; | ||
| /** Compare `this` to `that` per SemVer 2.0.0 precedence. Returns `-1`, `0`, or `1`. */ | ||
| compare(that: SemVer): -1 | 0 | 1; | ||
| /** Test whether `this > that`. */ | ||
| gt(that: SemVer): boolean; | ||
| /** Test whether `this >= that`. */ | ||
| gte(that: SemVer): boolean; | ||
| /** Test whether `this < that`. */ | ||
| lt(that: SemVer): boolean; | ||
| /** Test whether `this <= that`. */ | ||
| lte(that: SemVer): boolean; | ||
| /** Test whether `this` equals `that` (ignores build metadata). */ | ||
| equal(that: SemVer): boolean; | ||
| /** Alias for {@link equal}. */ | ||
| eq(that: SemVer): boolean; | ||
| /** Test whether `this` does not equal `that` (ignores build metadata). */ | ||
| neq(that: SemVer): boolean; | ||
| /** Whether this is a prerelease version. */ | ||
| get isPrerelease(): boolean; | ||
| /** Whether this is a stable (non-prerelease) version. */ | ||
| get isStable(): boolean; | ||
| private _bump; | ||
| /** Version bumping operations. */ | ||
| get bump(): SemVerBump; | ||
| [Equal.symbol](that: Equal.Equal): boolean; | ||
| [Hash.symbol](): number; | ||
| toString(): string; | ||
| toJSON(): unknown; | ||
| } | ||
| declare const SemVer_base: Schema.TaggedClass<SemVer, "SemVer", { | ||
| readonly _tag: Schema.tag<"SemVer">; | ||
| } & { | ||
| major: typeof Schema.Number; | ||
| minor: typeof Schema.Number; | ||
| patch: typeof Schema.Number; | ||
| prerelease: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>; | ||
| build: Schema.Array$<typeof Schema.String>; | ||
| }>; | ||
| /** Grouped bump operations returned by {@link SemVer.bump}. */ | ||
| export declare class SemVerBump { | ||
| private readonly v; | ||
| constructor(v: SemVer); | ||
| /** Bump major (resets minor, patch, prerelease, build). */ | ||
| major(): SemVer; | ||
| /** Bump minor (resets patch, prerelease, build). */ | ||
| minor(): SemVer; | ||
| /** Bump patch (resets prerelease, build). */ | ||
| patch(): SemVer; | ||
| /** Bump prerelease, optionally with a named identifier. */ | ||
| prerelease(id?: string): SemVer; | ||
| /** Strip prerelease and build, keeping major.minor.patch. */ | ||
| release(): SemVer; | ||
| } | ||
| declare const bumpMajor: (v: SemVer) => SemVer; | ||
| /** | ||
| * Effect {@link Order.Order} instance for {@link SemVer} following SemVer 2.0.0 | ||
| * precedence rules. Delegates to {@link SemVer.compare}. | ||
| * Increment the minor version and reset patch and prerelease to zero/empty. | ||
| * | ||
| * @see {@link SemVerOrderWithBuild} | ||
| * @see {@link https://semver.org/#spec-item-11 | SemVer 2.0.0 Section 11} | ||
| * @see {@link bumpMajor} | ||
| * @see {@link bumpPatch} | ||
| * @public | ||
| */ | ||
| export declare const SemVerOrder: Order.Order<SemVer>; | ||
| declare const bumpMinor: (v: SemVer) => SemVer; | ||
| /** | ||
| * Effect {@link Order.Order} instance for {@link SemVer} that additionally | ||
| * compares build metadata when versions are otherwise equal. | ||
| * Increment the patch version and clear prerelease identifiers. | ||
| * | ||
| * @see {@link SemVerOrder} | ||
| * @see {@link bumpMajor} | ||
| * @see {@link bumpMinor} | ||
| * @public | ||
| */ | ||
| export declare const SemVerOrderWithBuild: Order.Order<SemVer>; | ||
| declare const bumpPatch: (v: SemVer) => SemVer; | ||
| /** | ||
| * Effect service interface for parsing SemVer 2.0.0 strings. | ||
| * Increment the prerelease portion of a version. | ||
| * | ||
| * Provides effectful parsing of version strings, range expressions, and | ||
| * individual comparators. All parsing is strict SemVer 2.0.0; no loose mode | ||
| * or `v`-prefix coercion is supported. | ||
| * @param v - The version to bump. | ||
| * @param id - Optional prerelease identifier prefix (e.g., `"alpha"`, `"beta"`). | ||
| * | ||
| * Use {@link SemVerParserLive} to provide a concrete implementation. | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.0.0-alpha.3"); | ||
| * console.log(v.bump.prerelease("alpha").toString()); // "1.0.0-alpha.4" | ||
| * console.log(v.bump.prerelease("beta").toString()); // "1.0.0-beta.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link bumpRelease} | ||
| * @public | ||
| */ | ||
| declare const bumpPrerelease: (v: SemVer, id?: string) => SemVer; | ||
| /** | ||
| * Strip prerelease and build metadata, promoting a prerelease to its release version. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVerParser, SemVerParserLive } from "semver-effect"; | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const parser = yield* SemVerParser; | ||
| * const version = yield* parser.parseVersion("1.2.3"); | ||
| * const range = yield* parser.parseRange("^1.0.0"); | ||
| * const comp = yield* parser.parseComparator(">=2.0.0"); | ||
| * }).pipe(Effect.provide(SemVerParserLive)); | ||
| * const v = yield* SemVer.parse("1.2.3-rc.1"); | ||
| * console.log(v.bump.release().toString()); // "1.2.3" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVerParserLive} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @see {@link bumpPrerelease} | ||
| * @public | ||
| */ | ||
| export declare class SemVerParser extends SemVerParser_base { | ||
| } | ||
| declare const SemVerParser_base: Context.TagClass<SemVerParser, "semver-effect/SemVerParser", { | ||
| /** Parse a string into a {@link SemVer}. Fails with {@link InvalidVersionError} on invalid input. */ | ||
| readonly parseVersion: (input: string) => Effect_2.Effect<SemVer, InvalidVersionError>; | ||
| /** Parse a range expression into a {@link Range}. Fails with {@link InvalidRangeError} on invalid input. */ | ||
| readonly parseRange: (input: string) => Effect_2.Effect<Range, InvalidRangeError>; | ||
| /** Parse a single comparator string into a {@link Comparator}. Fails with {@link InvalidComparatorError} on invalid input. */ | ||
| readonly parseComparator: (input: string) => Effect_2.Effect<Comparator, InvalidComparatorError>; | ||
| }>; | ||
| declare const bumpRelease: (v: SemVer) => SemVer; | ||
| //#endregion | ||
| //#region src/utils/compare.d.ts | ||
| /** | ||
| * Live Effect {@link Layer} that provides the {@link SemVerParser} service. | ||
| * Compare two {@link SemVer} versions according to SemVer 2.0.0 precedence. | ||
| * | ||
| * This layer has no dependencies and can be provided directly. It delegates to | ||
| * the strict SemVer 2.0.0 recursive-descent parser implemented in the grammar | ||
| * module, with range normalization applied to parsed range expressions. | ||
| * Returns `-1` if `self` is lower, `0` if equal, `1` if higher. Build metadata | ||
| * is ignored per the spec. Supports both data-last and data-first calling styles. | ||
| * | ||
| * For the instance method alternative, use `v.compare(other)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVerParser, SemVerParserLive } from "semver-effect"; | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const parser = yield* SemVerParser; | ||
| * const v = yield* parser.parseVersion("1.0.0"); | ||
| * }).pipe(Effect.provide(SemVerParserLive)); | ||
| * const a = yield* SemVer.parse("1.0.0"); | ||
| * const b = yield* SemVer.parse("2.0.0"); | ||
| * console.log(a.compare(b)); // -1 (instance method) | ||
| * console.log(compare(a, b)); // -1 (standalone function) | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link SemVerParser} | ||
| * @see {@link https://effect.website/docs/context-management/layers | Effect Layers} | ||
| * @see {@link SemVerOrder} | ||
| * @see {@link https://semver.org/#spec-item-11 | SemVer 2.0.0 Section 11} | ||
| * @public | ||
| */ | ||
| export declare const SemVerParserLive: Layer.Layer<SemVerParser>; | ||
| declare const compare: { | ||
| (that: SemVer): (self: SemVer) => -1 | 0 | 1; | ||
| (self: SemVer, that: SemVer): -1 | 0 | 1; | ||
| }; | ||
| /** | ||
| * Remove redundant comparator sets from a {@link Range}. | ||
| * Test whether two {@link SemVer} versions are equal. | ||
| * | ||
| * A comparator set is considered redundant if another set in the range is a | ||
| * subset of it (i.e., the other set is more restrictive and already covered). | ||
| * Returns a new range with only the non-redundant sets. | ||
| * Uses Effect structural equality (`Equal.equals`), which compares | ||
| * `major.minor.patch` and prerelease identifiers but ignores build metadata. | ||
| * | ||
| * @see {@link isSubset} | ||
| * @see {@link equivalent} | ||
| * For the instance method alternative, use `v.eq(other)`. | ||
| * | ||
| * @see {@link neq} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| export declare const simplify: (range: Range) => Range; | ||
| declare const equal: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** | ||
| * Sort an array of {@link SemVer} versions in ascending order. | ||
| * Test whether `self` is strictly greater than `that`. | ||
| * | ||
| * Returns a new array; the input is not mutated. | ||
| * For the instance method alternative, use `v.gt(other)`. | ||
| * | ||
| * @see {@link rsort} | ||
| * @see {@link SemVerOrder} | ||
| * @see {@link gte} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| export declare const sort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| declare const gt: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** | ||
| * Test whether `self` is greater than or equal to `that`. | ||
| * | ||
| * For the instance method alternative, use `v.gte(other)`. | ||
| * | ||
| * @see {@link gt} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| declare const gte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** | ||
| * Test whether `self` is strictly less than `that`. | ||
| * | ||
| * For the instance method alternative, use `v.lt(other)`. | ||
| * | ||
| * @see {@link lte} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| declare const lt: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** | ||
| * Test whether `self` is less than or equal to `that`. | ||
| * | ||
| * For the instance method alternative, use `v.lte(other)`. | ||
| * | ||
| * @see {@link lt} | ||
| * @see {@link compare} | ||
| * @public | ||
| */ | ||
| declare const lte: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** | ||
| * Test whether two {@link SemVer} versions are not equal. | ||
| * | ||
| * For the instance method alternative, use `v.neq(other)`. | ||
| * | ||
| * @see {@link equal} | ||
| * @public | ||
| */ | ||
| declare const neq: { | ||
| (that: SemVer): (self: SemVer) => boolean; | ||
| (self: SemVer, that: SemVer): boolean; | ||
| }; | ||
| /** | ||
| * Test whether a version has prerelease identifiers. | ||
| * | ||
| * For the instance getter alternative, use `v.isPrerelease`. | ||
| * | ||
| * @see {@link isStable} | ||
| * @public | ||
| */ | ||
| declare const isPrerelease: (v: SemVer) => boolean; | ||
| /** | ||
| * Test whether a version is a stable release (no prerelease identifiers). | ||
| * | ||
| * For the instance getter alternative, use `v.isStable`. | ||
| * | ||
| * @see {@link isPrerelease} | ||
| * @public | ||
| */ | ||
| declare const isStable: (v: SemVer) => boolean; | ||
| /** | ||
| * Strip prerelease and/or build metadata from a version. | ||
@@ -1092,334 +1107,301 @@ * | ||
| * ``` | ||
| * | ||
| * @public | ||
| */ | ||
| export declare const truncate: { | ||
| (level: "prerelease" | "build"): (v: SemVer) => SemVer; | ||
| (v: SemVer, level: "prerelease" | "build"): SemVer; | ||
| declare const truncate: { | ||
| (level: "prerelease" | "build"): (v: SemVer) => SemVer; | ||
| (v: SemVer, level: "prerelease" | "build"): SemVer; | ||
| }; | ||
| /** | ||
| * Combine two {@link Range}s with OR semantics (union of comparator sets). | ||
| * Sort an array of {@link SemVer} versions in ascending order. | ||
| * | ||
| * The resulting range matches any version that satisfies either input range. | ||
| * Internally, this concatenates the comparator sets from both ranges. | ||
| * Returns a new array; the input is not mutated. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseRange, union } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* parseRange("^1.0.0"); | ||
| * const b = yield* parseRange("^2.0.0"); | ||
| * const combined = union(a, b); | ||
| * // combined matches versions satisfying ^1.0.0 OR ^2.0.0 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link intersect} | ||
| * @see {@link equivalent} | ||
| * @see {@link rsort} | ||
| * @see {@link SemVerOrder} | ||
| * @public | ||
| */ | ||
| export declare const union: { | ||
| (b: Range): (a: Range) => Range; | ||
| (a: Range, b: Range): Range; | ||
| }; | ||
| declare const sort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| /** | ||
| * Indicates that the intersection of two or more {@link Range}s produces an | ||
| * empty (unsatisfiable) result. | ||
| * Sort an array of {@link SemVer} versions in descending order. | ||
| * | ||
| * Returned by {@link intersect} when no comparator set in the cross-product | ||
| * of the input ranges is satisfiable. | ||
| * Returns a new array; the input is not mutated. | ||
| * | ||
| * @see {@link intersect} | ||
| * @see {@link Range} | ||
| * @see {@link sort} | ||
| * @see {@link SemVerOrder} | ||
| * @public | ||
| */ | ||
| export declare class UnsatisfiableConstraintError extends UnsatisfiableConstraintErrorBase<{ | ||
| /** The ranges that were being intersected. */ | ||
| readonly constraints: ReadonlyArray<Range>; | ||
| }> { | ||
| get message(): string; | ||
| } | ||
| declare const rsort: (versions: ReadonlyArray<SemVer>) => Array<SemVer>; | ||
| /** | ||
| * Tagged error base for {@link UnsatisfiableConstraintError}. | ||
| * Find the highest version in an array. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `UnsatisfiableConstraintError` appears in public type signatures. | ||
| * Consumers should use {@link UnsatisfiableConstraintError} directly. | ||
| * Returns `Option.none()` for an empty array. | ||
| * | ||
| * @internal | ||
| * @see {@link min} | ||
| * @see {@link sort} | ||
| * @public | ||
| */ | ||
| export declare const UnsatisfiableConstraintErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "UnsatisfiableConstraintError"; | ||
| } & Readonly<A>; | ||
| declare const max: (versions: ReadonlyArray<SemVer>) => Option.Option<SemVer>; | ||
| /** | ||
| * Indicates that no version in a given set satisfies a {@link Range}. | ||
| * Find the lowest version in an array. | ||
| * | ||
| * Returned by {@link VersionCache.resolve} and {@link VersionCache.resolveString} | ||
| * when the cache contains versions but none match the requested range. | ||
| * Returns `Option.none()` for an empty array. | ||
| * | ||
| * @see {@link VersionCache} | ||
| * @see {@link Range} | ||
| * @see {@link max} | ||
| * @see {@link sort} | ||
| * @public | ||
| */ | ||
| export declare class UnsatisfiedRangeError extends UnsatisfiedRangeErrorBase<{ | ||
| /** The range that could not be satisfied. */ | ||
| readonly range: Range; | ||
| /** The versions that were available for matching. */ | ||
| readonly available: ReadonlyArray<SemVer>; | ||
| }> { | ||
| get message(): string; | ||
| } | ||
| declare const min: (versions: ReadonlyArray<SemVer>) => Option.Option<SemVer>; | ||
| /** | ||
| * Tagged error base for {@link UnsatisfiedRangeError}. | ||
| * Compare two versions including build metadata. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `UnsatisfiedRangeError` appears in public type signatures. | ||
| * Consumers should use {@link UnsatisfiedRangeError} directly. | ||
| * This extends the standard {@link compare} by additionally comparing build | ||
| * metadata lexicographically when versions are otherwise equal. Per SemVer 2.0.0, | ||
| * build metadata SHOULD be ignored for precedence, but this function is provided | ||
| * for cases where a total ordering including build metadata is needed. | ||
| * | ||
| * @internal | ||
| * @see {@link compare} | ||
| * @see {@link SemVerOrderWithBuild} | ||
| * @public | ||
| */ | ||
| export declare const UnsatisfiedRangeErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "UnsatisfiedRangeError"; | ||
| } & Readonly<A>; | ||
| declare const compareWithBuild: { | ||
| (that: SemVer): (self: SemVer) => -1 | 0 | 1; | ||
| (self: SemVer, that: SemVer): -1 | 0 | 1; | ||
| }; | ||
| //#endregion | ||
| //#region src/utils/diff.d.ts | ||
| /** | ||
| * Effect service interface for an in-memory sorted version cache. | ||
| * Compute the difference between two {@link SemVer} versions. | ||
| * | ||
| * Provides mutation, query, resolution, grouping, and navigation operations | ||
| * over a set of {@link SemVer} versions. Versions are maintained in sorted | ||
| * order using {@link SemVerOrder}. | ||
| * Returns a {@link VersionDiff} containing the type of change (major, minor, | ||
| * patch, prerelease, build, or none) and signed numeric deltas for each field. | ||
| * | ||
| * Use {@link VersionCacheLive} to provide a concrete implementation backed by | ||
| * an Effect `Ref` of a `SortedSet`. | ||
| * Also available as `SemVer.diff(a, b)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer, VersionCache, VersionCacheLive, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * import { SemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const cache = yield* VersionCache; | ||
| * const v1 = new SemVer({ major: 1, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * const v2 = new SemVer({ major: 2, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * yield* cache.load([v1, v2]); | ||
| * const latest = yield* cache.latest(); | ||
| * console.log(latest.toString()); // "2.0.0" | ||
| * }).pipe(Effect.provide(Layer.merge(VersionCacheLive, SemVerParserLive))); | ||
| * const a = yield* SemVer.parse("1.2.3"); | ||
| * const b = yield* SemVer.parse("1.3.0"); | ||
| * const d = SemVer.diff(a, b); // static method | ||
| * console.log(d.type); // "minor" | ||
| * console.log(d.minor); // 1 | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionCacheLive} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @see {@link VersionDiff} | ||
| * @public | ||
| */ | ||
| export declare class VersionCache extends VersionCache_base { | ||
| } | ||
| declare const VersionCache_base: Context.TagClass<VersionCache, "semver-effect/VersionCache", { | ||
| /** Replace all cached versions with the given array. */ | ||
| readonly load: (versions: ReadonlyArray<SemVer>) => Effect_2.Effect<void, never>; | ||
| /** Add a single version to the cache. */ | ||
| readonly add: (version: SemVer) => Effect_2.Effect<void, never>; | ||
| /** Remove a single version from the cache. */ | ||
| readonly remove: (version: SemVer) => Effect_2.Effect<void, never>; | ||
| /** Retrieve all cached versions in sorted order. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly versions: Effect_2.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; | ||
| /** Retrieve the highest version in the cache. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly latest: () => Effect_2.Effect<SemVer, EmptyCacheError>; | ||
| /** Retrieve the lowest version in the cache. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly oldest: () => Effect_2.Effect<SemVer, EmptyCacheError>; | ||
| /** Find the highest version satisfying a {@link Range}. Fails with {@link UnsatisfiedRangeError} if none match. */ | ||
| readonly resolve: (range: Range) => Effect_2.Effect<SemVer, UnsatisfiedRangeError>; | ||
| /** Parse a range string and resolve it. Fails with {@link InvalidRangeError} or {@link UnsatisfiedRangeError}. */ | ||
| readonly resolveString: (input: string) => Effect_2.Effect<SemVer, InvalidRangeError | UnsatisfiedRangeError>; | ||
| /** | ||
| * Return all cached versions satisfying a {@link Range}. | ||
| * Fails with {@link EmptyCacheError} if the cache is empty (nothing loaded). | ||
| * Returns an empty array if the cache is non-empty but no versions match. | ||
| */ | ||
| readonly filter: (range: Range) => Effect_2.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; | ||
| /** Group cached versions by major, minor, or patch level. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly groupBy: (strategy: "major" | "minor" | "patch") => Effect_2.Effect<Map<string, ReadonlyArray<SemVer>>, EmptyCacheError>; | ||
| /** Return the latest version for each distinct major version. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly latestByMajor: () => Effect_2.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; | ||
| /** Return the latest version for each distinct major.minor pair. Fails with {@link EmptyCacheError} if empty. */ | ||
| readonly latestByMinor: () => Effect_2.Effect<ReadonlyArray<SemVer>, EmptyCacheError>; | ||
| /** Compute a {@link VersionDiff} between two versions. Fails with {@link VersionNotFoundError} if either is missing. */ | ||
| readonly diff: (a: SemVer, b: SemVer) => Effect_2.Effect<VersionDiff, VersionNotFoundError>; | ||
| /** Get the next higher version after the given one. Fails with {@link VersionNotFoundError} if not in cache. */ | ||
| readonly next: (version: SemVer) => Effect_2.Effect<Option_2.Option<SemVer>, VersionNotFoundError>; | ||
| /** Get the next lower version before the given one. Fails with {@link VersionNotFoundError} if not in cache. */ | ||
| readonly prev: (version: SemVer) => Effect_2.Effect<Option_2.Option<SemVer>, VersionNotFoundError>; | ||
| }>; | ||
| declare const diff: { | ||
| (b: SemVer): (a: SemVer) => VersionDiff; | ||
| (a: SemVer, b: SemVer): VersionDiff; | ||
| }; | ||
| //#endregion | ||
| //#region src/utils/grammar.d.ts | ||
| /** | ||
| * Live Effect {@link Layer} that provides the {@link VersionCache} service. | ||
| * Parse a string into a {@link SemVer} using a strict SemVer 2.0.0 | ||
| * recursive-descent parser. | ||
| * | ||
| * Backed by an Effect `Ref` containing a `SortedSet` ordered by {@link SemVerOrder}. | ||
| * Requires a {@link SemVerParser} in its dependency graph (used by `resolveString` | ||
| * to parse range strings). | ||
| * Rejects `v`/`V` prefixes, `=` prefixes, leading zeros on numeric identifiers, | ||
| * and any input that does not fully consume as a valid `major.minor.patch[-prerelease][+build]` | ||
| * string. Returns an `Effect.Effect` that fails with {@link InvalidVersionError} | ||
| * on invalid input. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { SemVer, VersionCache, VersionCacheLive, SemVerParserLive } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * import { parseValidSemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const AppLayer = Layer.merge(VersionCacheLive, SemVerParserLive); | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const cache = yield* VersionCache; | ||
| * const v = new SemVer({ major: 1, minor: 0, patch: 0, prerelease: [], build: [] }); | ||
| * yield* cache.add(v); | ||
| * const latest = yield* cache.latest(); | ||
| * }).pipe(Effect.provide(AppLayer)); | ||
| * const v = yield* parseValidSemVer("1.2.3-beta.1+build.42"); | ||
| * console.log(v.toString()); // "1.2.3-beta.1+build.42" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionCache} | ||
| * @see {@link SemVerParserLive} | ||
| * @see {@link https://effect.website/docs/context-management/layers | Effect Layers} | ||
| * @see {@link SemVer} | ||
| * @see {@link InvalidVersionError} | ||
| * @see {@link https://semver.org | SemVer 2.0.0 Specification} | ||
| * @public | ||
| */ | ||
| export declare const VersionCacheLive: Layer.Layer<VersionCache, never, SemVerParser>; | ||
| declare const parseValidSemVer: (raw: string) => Effect.Effect<SemVer, InvalidVersionError>; | ||
| /** | ||
| * The result of computing the difference between two {@link SemVer} versions. | ||
| * Parse a string into a single {@link Comparator}. | ||
| * | ||
| * Contains the classification of the change (`type`) along with the signed | ||
| * numeric deltas for `major`, `minor`, and `patch` fields. The `from` and `to` | ||
| * fields preserve the original version objects. | ||
| * Accepts an optional operator prefix (`=`, `>`, `>=`, `<`, `<=`) followed by | ||
| * a complete `major.minor.patch[-prerelease][+build]` version string. Wildcards | ||
| * and range syntax (tilde, caret, hyphen, X-range) are not allowed. | ||
| * | ||
| * The `type` field indicates the highest-precedence field that differs: | ||
| * - `"major"` — major versions differ | ||
| * - `"minor"` — major is equal, minor versions differ | ||
| * - `"patch"` — major and minor are equal, patch versions differ | ||
| * - `"prerelease"` — only prerelease identifiers differ | ||
| * - `"build"` — only build metadata differs | ||
| * - `"none"` — versions are identical | ||
| * If no operator is specified, `"="` (exact match) is assumed. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseValidSemVer, diff } from "semver-effect"; | ||
| * import { parseSingleComparator } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const a = yield* parseValidSemVer("1.2.3"); | ||
| * const b = yield* parseValidSemVer("2.0.0"); | ||
| * const d = diff(a, b); | ||
| * console.log(d.type); // "major" | ||
| * console.log(d.major); // 1 | ||
| * const comp = yield* parseSingleComparator(">=1.0.0"); | ||
| * console.log(comp.operator); // ">=" | ||
| * console.log(comp.version.toString()); // "1.0.0" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link diff} | ||
| * @see {@link SemVer} | ||
| * @see {@link Comparator} | ||
| * @see {@link InvalidComparatorError} | ||
| * @public | ||
| */ | ||
| export declare class VersionDiff extends VersionDiff_base { | ||
| toString(): string; | ||
| toJSON(): unknown; | ||
| } | ||
| declare const VersionDiff_base: Schema.TaggedClass<VersionDiff, "VersionDiff", { | ||
| readonly _tag: Schema.tag<"VersionDiff">; | ||
| } & { | ||
| type: Schema.Literal<["major", "minor", "patch", "prerelease", "build", "none"]>; | ||
| from: typeof SemVer; | ||
| to: typeof SemVer; | ||
| major: typeof Schema.Number; | ||
| minor: typeof Schema.Number; | ||
| patch: typeof Schema.Number; | ||
| }>; | ||
| declare const parseSingleComparator: (raw: string) => Effect.Effect<Comparator, InvalidComparatorError>; | ||
| //#endregion | ||
| //#region src/utils/matching.d.ts | ||
| /** | ||
| * Effect service interface for fetching available versions from an external source. | ||
| * Test whether a {@link SemVer} version satisfies a {@link Range}. | ||
| * | ||
| * Implementations might query a package registry (e.g., npm, GitHub Releases) and | ||
| * return parsed {@link SemVer} instances. This is an interface-only service; no | ||
| * default live layer is provided by this library -- consumers must supply their own. | ||
| * For the instance method alternative, use `range.test(version)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import type { VersionFetcher } from "semver-effect"; | ||
| * import { Effect, Layer } from "effect"; | ||
| * import { SemVer, Range } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * // Example: a stub implementation | ||
| * const StubFetcher = Layer.succeed(VersionFetcher, { | ||
| * fetch: (_packageName) => Effect.succeed([]), | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* SemVer.parse("1.5.0"); | ||
| * const r = yield* Range.parse("^1.0.0"); | ||
| * console.log(r.test(v)); // true (instance method) | ||
| * console.log(satisfies(v, r)); // true (standalone function) | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link VersionFetchError} | ||
| * @see {@link VersionCache} | ||
| * @see {@link https://effect.website/docs/context-management/services | Effect Services} | ||
| * @see {@link filter} | ||
| * @see {@link maxSatisfying} | ||
| * @see {@link minSatisfying} | ||
| * @public | ||
| */ | ||
| export declare class VersionFetcher extends VersionFetcher_base { | ||
| } | ||
| declare const VersionFetcher_base: Context.TagClass<VersionFetcher, "semver-effect/VersionFetcher", { | ||
| /** Fetch all available versions for the given package name. Fails with {@link VersionFetchError} on failure. */ | ||
| readonly fetch: (packageName: string) => Effect_2.Effect<ReadonlyArray<SemVer>, VersionFetchError>; | ||
| }>; | ||
| declare const satisfies: { | ||
| (range: Range): (version: SemVer) => boolean; | ||
| (version: SemVer, range: Range): boolean; | ||
| }; | ||
| /** | ||
| * Indicates that fetching versions from an external source failed. | ||
| * Filter an array of {@link SemVer} versions to only those satisfying a {@link Range}. | ||
| * | ||
| * Returned by {@link VersionFetcher} implementations when a network request or | ||
| * registry lookup fails. The `source` field identifies the registry or endpoint, | ||
| * and `cause` may contain the underlying error. | ||
| * For the instance method alternative, use `range.filter(versions)`. | ||
| * | ||
| * @see {@link VersionFetcher} | ||
| * @see {@link satisfies} | ||
| * @public | ||
| */ | ||
| export declare class VersionFetchError extends VersionFetchErrorBase<{ | ||
| /** The source identifier (e.g., registry URL or package name). */ | ||
| readonly source: string; | ||
| /** A human-readable description of the failure. */ | ||
| readonly message: string; | ||
| /** The underlying error, if any. */ | ||
| readonly cause?: unknown; | ||
| }> { | ||
| } | ||
| declare const filter: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => ReadonlyArray<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): ReadonlyArray<SemVer>; | ||
| }; | ||
| /** | ||
| * Tagged error base for {@link VersionFetchError}. | ||
| * Find the highest {@link SemVer} version satisfying a {@link Range}. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `VersionFetchError` appears in public type signatures. | ||
| * Consumers should use {@link VersionFetchError} directly. | ||
| * Returns `Option.none()` if no version satisfies the range. | ||
| * | ||
| * @internal | ||
| * @see {@link minSatisfying} | ||
| * @see {@link satisfies} | ||
| * @public | ||
| */ | ||
| export declare const VersionFetchErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "VersionFetchError"; | ||
| } & Readonly<A>; | ||
| declare const maxSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => Option.Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): Option.Option<SemVer>; | ||
| }; | ||
| /** | ||
| * Indicates that a specific version was not found in the {@link VersionCache}. | ||
| * Find the lowest {@link SemVer} version satisfying a {@link Range}. | ||
| * | ||
| * Returned by navigation operations (`diff`, `next`, `prev`) when the referenced | ||
| * version has not been loaded into the cache. | ||
| * Returns `Option.none()` if no version satisfies the range. | ||
| * | ||
| * @see {@link VersionCache} | ||
| * @see {@link maxSatisfying} | ||
| * @see {@link satisfies} | ||
| * @public | ||
| */ | ||
| export declare class VersionNotFoundError extends VersionNotFoundErrorBase<{ | ||
| /** The version that was not found in the cache. */ | ||
| readonly version: SemVer; | ||
| }> { | ||
| get message(): string; | ||
| } | ||
| declare const minSatisfying: { | ||
| (range: Range): (versions: ReadonlyArray<SemVer>) => Option.Option<SemVer>; | ||
| (versions: ReadonlyArray<SemVer>, range: Range): Option.Option<SemVer>; | ||
| }; | ||
| //#endregion | ||
| //#region src/utils/order.d.ts | ||
| /** | ||
| * Tagged error base for {@link VersionNotFoundError}. | ||
| * Effect `Order.Order` instance for {@link SemVer} following SemVer 2.0.0 | ||
| * precedence rules. Delegates to `SemVer`'s `compare` instance method. | ||
| * | ||
| * @privateRemarks | ||
| * Exported because TypeScript declaration bundling requires the base class to be | ||
| * accessible when `VersionNotFoundError` appears in public type signatures. | ||
| * Consumers should use {@link VersionNotFoundError} directly. | ||
| * @see {@link SemVerOrderWithBuild} | ||
| * @see {@link https://semver.org/#spec-item-11 | SemVer 2.0.0 Section 11} | ||
| * @public | ||
| */ | ||
| declare const SemVerOrder: Order.Order<SemVer>; | ||
| /** | ||
| * Effect `Order.Order` instance for {@link SemVer} that additionally | ||
| * compares build metadata when versions are otherwise equal. | ||
| * | ||
| * @internal | ||
| * @see {@link SemVerOrder} | ||
| * @public | ||
| */ | ||
| export declare const VersionNotFoundErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & { | ||
| readonly _tag: "VersionNotFoundError"; | ||
| } & Readonly<A>; | ||
| export { } | ||
| declare const SemVerOrderWithBuild: Order.Order<SemVer>; | ||
| //#endregion | ||
| //#region src/utils/parseRange.d.ts | ||
| /** | ||
| * Parse a SemVer range expression string and normalize the result. | ||
| * | ||
| * This is a convenience function that performs the same parsing and normalization | ||
| * as `SemVerParser`'s `parseRange` method but without requiring the service in scope. | ||
| * Returns an `Effect.Effect` that fails with {@link InvalidRangeError} | ||
| * on invalid input. | ||
| * | ||
| * Supports hyphen ranges (`1.0.0 - 2.0.0`), X-ranges (`1.x`, `*`), tilde | ||
| * ranges (`~1.2.3`), caret ranges (`^1.2.3`), and `||`-separated unions. | ||
| * All version components must be strictly valid SemVer 2.0.0 (no loose parsing). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { parseRange, parseValidSemVer, satisfies } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const range = yield* parseRange("^1.0.0"); | ||
| * const v = yield* parseValidSemVer("1.5.0"); | ||
| * console.log(satisfies(v, range)); // true | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Range} | ||
| * @see {@link SemVerParser} | ||
| * @see {@link InvalidRangeError} | ||
| * @public | ||
| */ | ||
| declare const parseRange: (input: string) => Effect.Effect<Range, InvalidRangeError>; | ||
| //#endregion | ||
| //#region src/utils/prettyPrint.d.ts | ||
| /** | ||
| * Union of all schema types that can be pretty-printed by {@link prettyPrint}. | ||
| * | ||
| * @see {@link prettyPrint} | ||
| * @public | ||
| */ | ||
| type Printable = SemVer | Comparator | Range | VersionDiff; | ||
| /** | ||
| * Convert any {@link Printable} schema value to its human-readable string form. | ||
| * | ||
| * Dispatches on the `_tag` field to call the appropriate `toString()` method. | ||
| * This is a convenience for logging and display; each schema type also has its | ||
| * own `toString()` method. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { prettyPrint, parseValidSemVer } from "semver-effect"; | ||
| * import { Effect } from "effect"; | ||
| * | ||
| * const program = Effect.gen(function* () { | ||
| * const v = yield* parseValidSemVer("1.2.3-alpha.1"); | ||
| * console.log(prettyPrint(v)); // "1.2.3-alpha.1" | ||
| * }); | ||
| * ``` | ||
| * | ||
| * @see {@link Printable} | ||
| * @public | ||
| */ | ||
| declare const prettyPrint: (value: Printable) => string; | ||
| //#endregion | ||
| export { Comparator, type ComparatorSet, EmptyCacheError, EmptyCacheErrorBase, InvalidBumpError, InvalidBumpErrorBase, InvalidComparatorError, InvalidComparatorErrorBase, InvalidPrereleaseError, InvalidPrereleaseErrorBase, InvalidRangeError, InvalidRangeErrorBase, InvalidVersionError, InvalidVersionErrorBase, type Printable, Range, SemVer, SemVerBump, SemVerOrder, SemVerOrderWithBuild, SemVerParser, SemVerParserLive, UnsatisfiableConstraintError, UnsatisfiableConstraintErrorBase, UnsatisfiedRangeError, UnsatisfiedRangeErrorBase, VersionCache, VersionCacheLive, VersionDiff, VersionFetchError, VersionFetchErrorBase, VersionFetcher, VersionNotFoundError, VersionNotFoundErrorBase, bumpMajor, bumpMinor, bumpPatch, bumpPrerelease, bumpRelease, compare, compareWithBuild, diff, equal, equivalent, filter, gt, gte, intersect, isPrerelease, isStable, isSubset, lt, lte, max, maxSatisfying, min, minSatisfying, neq, parseRange, parseSingleComparator, parseValidSemVer, prettyPrint, rsort, satisfies, simplify, sort, truncate, union }; | ||
| //# sourceMappingURL=index.d.ts.map |
+40
-1267
@@ -1,1268 +0,39 @@ | ||
| import { Context, Data, Effect, Equal, Function, Hash, Layer, Match, Option, Order, Ref, Schema, SortedSet } from "effect"; | ||
| const EmptyCacheErrorBase = Data.TaggedError("EmptyCacheError"); | ||
| class EmptyCacheError extends EmptyCacheErrorBase { | ||
| get message() { | ||
| return "Version cache is empty"; | ||
| } | ||
| } | ||
| const InvalidBumpErrorBase = Data.TaggedError("InvalidBumpError"); | ||
| class InvalidBumpError extends InvalidBumpErrorBase { | ||
| get message() { | ||
| return `Cannot apply ${this.type} bump to version ${this.version.toString()}`; | ||
| } | ||
| } | ||
| const InvalidComparatorErrorBase = Data.TaggedError("InvalidComparatorError"); | ||
| class InvalidComparatorError extends InvalidComparatorErrorBase { | ||
| get message() { | ||
| const base = `Invalid comparator: "${this.input}"`; | ||
| return void 0 !== this.position ? `${base} at position ${this.position}` : base; | ||
| } | ||
| } | ||
| const InvalidPrereleaseErrorBase = Data.TaggedError("InvalidPrereleaseError"); | ||
| class InvalidPrereleaseError extends InvalidPrereleaseErrorBase { | ||
| get message() { | ||
| return `Invalid prerelease identifier: "${this.input}"`; | ||
| } | ||
| } | ||
| const InvalidRangeErrorBase = Data.TaggedError("InvalidRangeError"); | ||
| class InvalidRangeError extends InvalidRangeErrorBase { | ||
| get message() { | ||
| const base = `Invalid range expression: "${this.input}"`; | ||
| return void 0 !== this.position ? `${base} at position ${this.position}` : base; | ||
| } | ||
| } | ||
| const InvalidVersionErrorBase = Data.TaggedError("InvalidVersionError"); | ||
| class InvalidVersionError extends InvalidVersionErrorBase { | ||
| get message() { | ||
| const base = `Invalid version string: "${this.input}"`; | ||
| return void 0 !== this.position ? `${base} at position ${this.position}` : base; | ||
| } | ||
| } | ||
| const UnsatisfiableConstraintErrorBase = Data.TaggedError("UnsatisfiableConstraintError"); | ||
| class UnsatisfiableConstraintError extends UnsatisfiableConstraintErrorBase { | ||
| get message() { | ||
| const count = this.constraints.length; | ||
| return `No version satisfies all ${count} constraint${1 === count ? "" : "s"}`; | ||
| } | ||
| } | ||
| const UnsatisfiedRangeErrorBase = Data.TaggedError("UnsatisfiedRangeError"); | ||
| class UnsatisfiedRangeError extends UnsatisfiedRangeErrorBase { | ||
| get message() { | ||
| const count = this.available.length; | ||
| return `No version satisfies range ${this.range.toString()} (${count} version${1 === count ? "" : "s"} available)`; | ||
| } | ||
| } | ||
| const VersionFetchErrorBase = Data.TaggedError("VersionFetchError"); | ||
| class VersionFetchError extends VersionFetchErrorBase { | ||
| } | ||
| const VersionNotFoundErrorBase = Data.TaggedError("VersionNotFoundError"); | ||
| class VersionNotFoundError extends VersionNotFoundErrorBase { | ||
| get message() { | ||
| return `Version not found in cache: ${this.version.toString()}`; | ||
| } | ||
| } | ||
| class SemVerParser extends Context.Tag("semver-effect/SemVerParser")() { | ||
| } | ||
| var _computedKey, _computedKey1, _computedKey2; | ||
| const comparePre = (a, b)=>{ | ||
| if ("number" == typeof a && "number" == typeof b) return a - b; | ||
| if ("string" == typeof a && "string" == typeof b) return a < b ? -1 : a > b ? 1 : 0; | ||
| if ("number" == typeof a) return -1; | ||
| return 1; | ||
| }; | ||
| _computedKey = Equal.symbol, _computedKey1 = Hash.symbol, _computedKey2 = Symbol.for("nodejs.util.inspect.custom"); | ||
| class SemVer extends Schema.TaggedClass()("SemVer", { | ||
| major: Schema.Number, | ||
| minor: Schema.Number, | ||
| patch: Schema.Number, | ||
| prerelease: Schema.Array(Schema.Union(Schema.String, Schema.Number)), | ||
| build: Schema.Array(Schema.String) | ||
| }) { | ||
| static parse; | ||
| static of; | ||
| static compare; | ||
| static gt; | ||
| static gte; | ||
| static lt; | ||
| static lte; | ||
| static neq; | ||
| static equal; | ||
| static diff; | ||
| static sort; | ||
| static rsort; | ||
| static max; | ||
| static min; | ||
| compare(that) { | ||
| if (this.major !== that.major) return this.major > that.major ? 1 : -1; | ||
| if (this.minor !== that.minor) return this.minor > that.minor ? 1 : -1; | ||
| if (this.patch !== that.patch) return this.patch > that.patch ? 1 : -1; | ||
| const aPre = this.prerelease; | ||
| const bPre = that.prerelease; | ||
| if (0 === aPre.length && 0 === bPre.length) return 0; | ||
| if (0 === aPre.length) return 1; | ||
| if (0 === bPre.length) return -1; | ||
| const len = Math.min(aPre.length, bPre.length); | ||
| for(let i = 0; i < len; i++){ | ||
| const cmp = comparePre(aPre[i], bPre[i]); | ||
| if (0 !== cmp) return cmp < 0 ? -1 : 1; | ||
| } | ||
| if (aPre.length !== bPre.length) return aPre.length > bPre.length ? 1 : -1; | ||
| return 0; | ||
| } | ||
| gt(that) { | ||
| return 1 === this.compare(that); | ||
| } | ||
| gte(that) { | ||
| return this.compare(that) >= 0; | ||
| } | ||
| lt(that) { | ||
| return -1 === this.compare(that); | ||
| } | ||
| lte(that) { | ||
| return this.compare(that) <= 0; | ||
| } | ||
| equal(that) { | ||
| return 0 === this.compare(that); | ||
| } | ||
| eq(that) { | ||
| return this.equal(that); | ||
| } | ||
| neq(that) { | ||
| return 0 !== this.compare(that); | ||
| } | ||
| get isPrerelease() { | ||
| return this.prerelease.length > 0; | ||
| } | ||
| get isStable() { | ||
| return 0 === this.prerelease.length; | ||
| } | ||
| _bump; | ||
| get bump() { | ||
| if (!this._bump) this._bump = new SemVerBump(this); | ||
| return this._bump; | ||
| } | ||
| [_computedKey](that) { | ||
| if (!(that instanceof SemVer)) return false; | ||
| return this.major === that.major && this.minor === that.minor && this.patch === that.patch && this.prerelease.length === that.prerelease.length && this.prerelease.every((v, i)=>v === that.prerelease[i]); | ||
| } | ||
| [_computedKey1]() { | ||
| let h = Hash.hash(this.major); | ||
| h = Hash.combine(h)(Hash.hash(this.minor)); | ||
| h = Hash.combine(h)(Hash.hash(this.patch)); | ||
| for (const item of this.prerelease)h = Hash.combine(h)(Hash.hash(item)); | ||
| return Hash.cached(this)(h); | ||
| } | ||
| toString() { | ||
| let s = `${this.major}.${this.minor}.${this.patch}`; | ||
| if (this.prerelease.length > 0) s += `-${this.prerelease.join(".")}`; | ||
| if (this.build.length > 0) s += `+${this.build.join(".")}`; | ||
| return s; | ||
| } | ||
| toJSON() { | ||
| return { | ||
| _tag: "SemVer", | ||
| major: this.major, | ||
| minor: this.minor, | ||
| patch: this.patch, | ||
| prerelease: this.prerelease.slice(), | ||
| build: this.build.slice() | ||
| }; | ||
| } | ||
| [_computedKey2]() { | ||
| return this.toString(); | ||
| } | ||
| } | ||
| class SemVerBump { | ||
| v; | ||
| constructor(v){ | ||
| this.v = v; | ||
| } | ||
| major() { | ||
| return new SemVer({ | ||
| major: this.v.major + 1, | ||
| minor: 0, | ||
| patch: 0, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| minor() { | ||
| return new SemVer({ | ||
| major: this.v.major, | ||
| minor: this.v.minor + 1, | ||
| patch: 0, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| patch() { | ||
| return new SemVer({ | ||
| major: this.v.major, | ||
| minor: this.v.minor, | ||
| patch: this.v.patch + 1, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| prerelease(id) { | ||
| const { major, minor, patch } = this.v; | ||
| const pre = this.v.prerelease; | ||
| if (0 === pre.length) return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch: patch + 1, | ||
| prerelease: void 0 !== id ? [ | ||
| id, | ||
| 0 | ||
| ] : [ | ||
| 0 | ||
| ], | ||
| build: [] | ||
| }); | ||
| if (void 0 !== id) { | ||
| const currentPrefix = "string" == typeof pre[0] ? pre[0] : null; | ||
| if (currentPrefix !== id) return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: [ | ||
| id, | ||
| 0 | ||
| ], | ||
| build: [] | ||
| }); | ||
| } | ||
| const last = pre[pre.length - 1]; | ||
| if ("number" == typeof last) { | ||
| const newPre = [ | ||
| ...pre | ||
| ]; | ||
| newPre[newPre.length - 1] = last + 1; | ||
| return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: newPre, | ||
| build: [] | ||
| }); | ||
| } | ||
| return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: [ | ||
| ...pre, | ||
| 0 | ||
| ], | ||
| build: [] | ||
| }); | ||
| } | ||
| release() { | ||
| return new SemVer({ | ||
| major: this.v.major, | ||
| minor: this.v.minor, | ||
| patch: this.v.patch, | ||
| prerelease: [], | ||
| build: [] | ||
| }); | ||
| } | ||
| } | ||
| var Comparator_computedKey; | ||
| Comparator_computedKey = Symbol.for("nodejs.util.inspect.custom"); | ||
| class Comparator extends Schema.TaggedClass()("Comparator", { | ||
| operator: Schema.Literal("=", ">", ">=", "<", "<="), | ||
| version: SemVer | ||
| }) { | ||
| static parse; | ||
| test(version) { | ||
| const cmp = version.compare(this.version); | ||
| switch(this.operator){ | ||
| case "=": | ||
| return 0 === cmp; | ||
| case ">": | ||
| return cmp > 0; | ||
| case ">=": | ||
| return cmp >= 0; | ||
| case "<": | ||
| return cmp < 0; | ||
| case "<=": | ||
| return cmp <= 0; | ||
| } | ||
| } | ||
| toString() { | ||
| const op = "=" === this.operator ? "" : this.operator; | ||
| return `${op}${this.version.toString()}`; | ||
| } | ||
| [Comparator_computedKey]() { | ||
| return this.toString(); | ||
| } | ||
| } | ||
| var Range_computedKey; | ||
| Range_computedKey = Symbol.for("nodejs.util.inspect.custom"); | ||
| class Range extends Schema.TaggedClass()("Range", { | ||
| sets: Schema.Array(Schema.Array(Comparator)) | ||
| }) { | ||
| static parse; | ||
| static satisfies; | ||
| static filter; | ||
| static maxSatisfying; | ||
| static minSatisfying; | ||
| test(version) { | ||
| return this.sets.some((set)=>satisfiesSet(version, set)); | ||
| } | ||
| filter(versions) { | ||
| return versions.filter((v)=>this.test(v)); | ||
| } | ||
| toString() { | ||
| return this.sets.map((set)=>set.map((c)=>c.toString()).join(" ")).join(" || "); | ||
| } | ||
| [Range_computedKey]() { | ||
| return this.toString(); | ||
| } | ||
| } | ||
| const satisfiesSet = (version, set)=>{ | ||
| if (0 === set.length) return true; | ||
| if (version.prerelease.length > 0) { | ||
| const hasTupleMatch = set.some((c)=>c.version.prerelease.length > 0 && c.version.major === version.major && c.version.minor === version.minor && c.version.patch === version.patch); | ||
| if (!hasTupleMatch) return false; | ||
| } | ||
| return set.every((c)=>c.test(version)); | ||
| }; | ||
| const desugar_sv = (major, minor, patch, prerelease = [], build = [])=>new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: prerelease.slice(), | ||
| build: build.slice() | ||
| }); | ||
| const desugar_comp = (operator, version)=>new Comparator({ | ||
| operator, | ||
| version | ||
| }); | ||
| const desugarTilde = (p)=>{ | ||
| const major = p.major ?? 0; | ||
| const minor = p.minor; | ||
| const patch = p.patch ?? 0; | ||
| if (null === minor) return [ | ||
| desugar_comp(">=", desugar_sv(major, 0, 0)), | ||
| desugar_comp("<", desugar_sv(major + 1, 0, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| return [ | ||
| desugar_comp(">=", desugar_sv(major, minor, patch, p.prerelease)), | ||
| desugar_comp("<", desugar_sv(major, minor + 1, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| }; | ||
| const desugarCaret = (p)=>{ | ||
| const major = p.major ?? 0; | ||
| const minor = p.minor; | ||
| const patch = p.patch; | ||
| const lower = desugar_sv(major, minor ?? 0, patch ?? 0, p.prerelease); | ||
| if (0 !== major) return [ | ||
| desugar_comp(">=", lower), | ||
| desugar_comp("<", desugar_sv(major + 1, 0, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| if (null === minor) return [ | ||
| desugar_comp(">=", lower), | ||
| desugar_comp("<", desugar_sv(1, 0, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| if (0 !== minor) return [ | ||
| desugar_comp(">=", lower), | ||
| desugar_comp("<", desugar_sv(0, minor + 1, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| if (null === patch) return [ | ||
| desugar_comp(">=", lower), | ||
| desugar_comp("<", desugar_sv(0, 1, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| if (0 !== patch) return [ | ||
| desugar_comp(">=", lower), | ||
| desugar_comp("<", desugar_sv(0, 0, patch + 1, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| return [ | ||
| desugar_comp(">=", lower), | ||
| desugar_comp("<", desugar_sv(0, 0, 1, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| }; | ||
| const desugarXRange = (operator, p)=>{ | ||
| const major = p.major; | ||
| const minor = p.minor; | ||
| const patch = p.patch; | ||
| if (null !== major && null !== minor && null !== patch) { | ||
| const version = desugar_sv(major, minor, patch, p.prerelease, p.build); | ||
| if (null === operator || "=" === operator) return [ | ||
| desugar_comp("=", version) | ||
| ]; | ||
| return [ | ||
| desugar_comp(operator, version) | ||
| ]; | ||
| } | ||
| if (null === major) return [ | ||
| desugar_comp(">=", desugar_sv(0, 0, 0)) | ||
| ]; | ||
| if (null === minor) { | ||
| if (null === operator || "" === operator || "=" === operator) return [ | ||
| desugar_comp(">=", desugar_sv(major, 0, 0)), | ||
| desugar_comp("<", desugar_sv(major + 1, 0, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| if (">" === operator) return [ | ||
| desugar_comp(">=", desugar_sv(major + 1, 0, 0)) | ||
| ]; | ||
| if (">=" === operator) return [ | ||
| desugar_comp(">=", desugar_sv(major, 0, 0)) | ||
| ]; | ||
| if ("<" === operator) return [ | ||
| desugar_comp("<", desugar_sv(major, 0, 0)) | ||
| ]; | ||
| if ("<=" === operator) return [ | ||
| desugar_comp("<", desugar_sv(major + 1, 0, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| } | ||
| if (null === operator || "" === operator || "=" === operator) return [ | ||
| desugar_comp(">=", desugar_sv(major, minor, 0)), | ||
| desugar_comp("<", desugar_sv(major, minor + 1, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| if (">" === operator) return [ | ||
| desugar_comp(">=", desugar_sv(major, minor + 1, 0)) | ||
| ]; | ||
| if (">=" === operator) return [ | ||
| desugar_comp(">=", desugar_sv(major, minor, 0)) | ||
| ]; | ||
| if ("<" === operator) return [ | ||
| desugar_comp("<", desugar_sv(major, minor, 0)) | ||
| ]; | ||
| return [ | ||
| desugar_comp("<", desugar_sv(major, minor + 1, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| }; | ||
| const desugarHyphen = (lower, upper)=>{ | ||
| const lowerVersion = desugar_sv(lower.major ?? 0, lower.minor ?? 0, lower.patch ?? 0, lower.prerelease); | ||
| if (null !== upper.major && null !== upper.minor && null !== upper.patch) { | ||
| const upperVersion = desugar_sv(upper.major, upper.minor, upper.patch, upper.prerelease); | ||
| return [ | ||
| desugar_comp(">=", lowerVersion), | ||
| desugar_comp("<=", upperVersion) | ||
| ]; | ||
| } | ||
| if (null !== upper.major && null !== upper.minor) return [ | ||
| desugar_comp(">=", lowerVersion), | ||
| desugar_comp("<", desugar_sv(upper.major, upper.minor + 1, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| if (null !== upper.major) return [ | ||
| desugar_comp(">=", lowerVersion), | ||
| desugar_comp("<", desugar_sv(upper.major + 1, 0, 0, [ | ||
| 0 | ||
| ])) | ||
| ]; | ||
| return [ | ||
| desugar_comp(">=", lowerVersion) | ||
| ]; | ||
| }; | ||
| const peek = (s)=>s.pos < s.len ? s.input[s.pos] : void 0; | ||
| const advance = (s)=>{ | ||
| if (s.pos < s.len) { | ||
| const ch = s.input[s.pos]; | ||
| s.pos++; | ||
| return ch; | ||
| } | ||
| }; | ||
| const isDigit = (ch)=>ch >= "0" && ch <= "9"; | ||
| const isLetter = (ch)=>ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z"; | ||
| const isIdentChar = (ch)=>isDigit(ch) || isLetter(ch) || "-" === ch; | ||
| const atEnd = (s)=>s.pos >= s.len; | ||
| const peekDigit = (s)=>{ | ||
| const ch = peek(s); | ||
| return void 0 !== ch && isDigit(ch); | ||
| }; | ||
| const peekIdentChar = (s)=>{ | ||
| const ch = peek(s); | ||
| return void 0 !== ch && isIdentChar(ch); | ||
| }; | ||
| const makeParseNumericIdentifier = (makeFail)=>(s)=>Effect.gen(function*() { | ||
| const start = s.pos; | ||
| const first = peek(s); | ||
| if (void 0 === first || !isDigit(first)) return yield* Effect.fail(makeFail(s)); | ||
| let digits = ""; | ||
| while(peekDigit(s))digits += advance(s); | ||
| if (digits.length > 1 && "0" === digits[0]) { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| const value = Number(digits); | ||
| if (!Number.isSafeInteger(value)) { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| return value; | ||
| }); | ||
| const makeParsePrereleaseIdentifier = (makeFail)=>(s)=>Effect.gen(function*() { | ||
| const start = s.pos; | ||
| let token = ""; | ||
| let hasNonDigit = false; | ||
| const first = peek(s); | ||
| if (void 0 === first || !isIdentChar(first)) return yield* Effect.fail(makeFail(s)); | ||
| while(peekIdentChar(s)){ | ||
| const ch = advance(s) ?? ""; | ||
| if (!isDigit(ch)) hasNonDigit = true; | ||
| token += ch; | ||
| } | ||
| if (0 === token.length) return yield* Effect.fail(makeFail(s)); | ||
| if (hasNonDigit) return token; | ||
| if (token.length > 1 && "0" === token[0]) { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| const value = Number(token); | ||
| if (!Number.isSafeInteger(value)) { | ||
| s.pos = start; | ||
| return yield* Effect.fail(makeFail(s, start)); | ||
| } | ||
| return value; | ||
| }); | ||
| const makeParseBuildIdentifier = (makeFail)=>(s)=>Effect.gen(function*() { | ||
| let token = ""; | ||
| const first = peek(s); | ||
| if (void 0 === first || !isIdentChar(first)) return yield* Effect.fail(makeFail(s)); | ||
| while(peekIdentChar(s))token += advance(s) ?? ""; | ||
| if (0 === token.length) return yield* Effect.fail(makeFail(s)); | ||
| return token; | ||
| }); | ||
| const makeParsePreRelease = (parsePrereleaseId)=>(s)=>Effect.gen(function*() { | ||
| const identifiers = []; | ||
| identifiers.push((yield* parsePrereleaseId(s))); | ||
| while(!atEnd(s) && "." === peek(s)){ | ||
| advance(s); | ||
| identifiers.push((yield* parsePrereleaseId(s))); | ||
| } | ||
| return identifiers; | ||
| }); | ||
| const makeParseBuild = (parseBuildId)=>(s)=>Effect.gen(function*() { | ||
| const identifiers = []; | ||
| identifiers.push((yield* parseBuildId(s))); | ||
| while(!atEnd(s) && "." === peek(s)){ | ||
| advance(s); | ||
| identifiers.push((yield* parseBuildId(s))); | ||
| } | ||
| return identifiers; | ||
| }); | ||
| const failVersion = (s, position)=>new InvalidVersionError({ | ||
| input: s.input, | ||
| position: position ?? s.pos | ||
| }); | ||
| const parseNumericIdentifier = makeParseNumericIdentifier(failVersion); | ||
| const parsePrereleaseIdentifier = makeParsePrereleaseIdentifier(failVersion); | ||
| const parseBuildIdentifier = makeParseBuildIdentifier(failVersion); | ||
| const parsePreRelease = makeParsePreRelease(parsePrereleaseIdentifier); | ||
| const parseBuild = makeParseBuild(parseBuildIdentifier); | ||
| const parseValidSemVer = (raw)=>Effect.gen(function*() { | ||
| const trimmed = raw.trim(); | ||
| const s = { | ||
| input: trimmed, | ||
| pos: 0, | ||
| len: trimmed.length | ||
| }; | ||
| if (0 === s.len) return yield* Effect.fail(new InvalidVersionError({ | ||
| input: raw, | ||
| position: 0 | ||
| })); | ||
| const first = peek(s); | ||
| if ("v" === first || "V" === first) return yield* Effect.fail(new InvalidVersionError({ | ||
| input: trimmed, | ||
| position: 0 | ||
| })); | ||
| if ("=" === first) return yield* Effect.fail(new InvalidVersionError({ | ||
| input: trimmed, | ||
| position: 0 | ||
| })); | ||
| const major = yield* parseNumericIdentifier(s); | ||
| if ("." !== peek(s)) return yield* Effect.fail(failVersion(s)); | ||
| advance(s); | ||
| const minor = yield* parseNumericIdentifier(s); | ||
| if ("." !== peek(s)) return yield* Effect.fail(failVersion(s)); | ||
| advance(s); | ||
| const patch = yield* parseNumericIdentifier(s); | ||
| let prerelease = []; | ||
| if (!atEnd(s) && "-" === peek(s)) { | ||
| advance(s); | ||
| prerelease = yield* parsePreRelease(s); | ||
| } | ||
| let build = []; | ||
| if (!atEnd(s) && "+" === peek(s)) { | ||
| advance(s); | ||
| build = yield* parseBuild(s); | ||
| } | ||
| if (!atEnd(s)) return yield* Effect.fail(failVersion(s)); | ||
| return new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease, | ||
| build | ||
| }); | ||
| }); | ||
| const failRange = (s, position)=>new InvalidRangeError({ | ||
| input: s.input, | ||
| position: position ?? s.pos | ||
| }); | ||
| const parseNumericIdentifierRange = makeParseNumericIdentifier(failRange); | ||
| const parsePrereleaseIdentifierRange = makeParsePrereleaseIdentifier(failRange); | ||
| const parseBuildIdentifierRange = makeParseBuildIdentifier(failRange); | ||
| const parsePrereleaseRange = makeParsePreRelease(parsePrereleaseIdentifierRange); | ||
| const parseBuildRange = makeParseBuild(parseBuildIdentifierRange); | ||
| const parseXR = (s)=>Effect.gen(function*() { | ||
| const ch = peek(s); | ||
| if ("x" === ch || "X" === ch || "*" === ch) { | ||
| advance(s); | ||
| return null; | ||
| } | ||
| return yield* parseNumericIdentifierRange(s); | ||
| }); | ||
| const parsePartial = (s)=>Effect.gen(function*() { | ||
| const major = yield* parseXR(s); | ||
| let minor = null; | ||
| let patch = null; | ||
| let prerelease = []; | ||
| let build = []; | ||
| if (!atEnd(s) && "." === peek(s)) { | ||
| advance(s); | ||
| minor = yield* parseXR(s); | ||
| if (!atEnd(s) && "." === peek(s)) { | ||
| advance(s); | ||
| patch = yield* parseXR(s); | ||
| if (null !== patch && !atEnd(s) && "-" === peek(s)) { | ||
| advance(s); | ||
| prerelease = yield* parsePrereleaseRange(s); | ||
| } | ||
| if (null !== patch && !atEnd(s) && "+" === peek(s)) { | ||
| advance(s); | ||
| build = yield* parseBuildRange(s); | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease, | ||
| build | ||
| }; | ||
| }); | ||
| const parseOperator = (s)=>{ | ||
| const ch = peek(s); | ||
| if (">" === ch) { | ||
| advance(s); | ||
| if ("=" === peek(s)) { | ||
| advance(s); | ||
| return ">="; | ||
| } | ||
| return ">"; | ||
| } | ||
| if ("<" === ch) { | ||
| advance(s); | ||
| if ("=" === peek(s)) { | ||
| advance(s); | ||
| return "<="; | ||
| } | ||
| return "<"; | ||
| } | ||
| if ("=" === ch) { | ||
| advance(s); | ||
| return "="; | ||
| } | ||
| return null; | ||
| }; | ||
| const skipSpaces = (s)=>{ | ||
| while(!atEnd(s) && " " === peek(s))advance(s); | ||
| }; | ||
| const isHyphenRange = (s)=>s.pos + 2 < s.len && " " === s.input[s.pos] && "-" === s.input[s.pos + 1] && " " === s.input[s.pos + 2]; | ||
| const isOrSeparator = (s)=>{ | ||
| let pos = s.pos; | ||
| while(pos < s.len && " " === s.input[pos])pos++; | ||
| return pos + 1 < s.len && "|" === s.input[pos] && "|" === s.input[pos + 1]; | ||
| }; | ||
| const consumeOrSeparator = (s)=>{ | ||
| while(!atEnd(s) && " " === peek(s))advance(s); | ||
| advance(s); | ||
| advance(s); | ||
| while(!atEnd(s) && " " === peek(s))advance(s); | ||
| }; | ||
| const parseSimple = (s)=>Effect.gen(function*() { | ||
| const ch = peek(s); | ||
| if ("~" === ch) { | ||
| advance(s); | ||
| if (">" === peek(s)) return yield* Effect.fail(failRange(s)); | ||
| const partial = yield* parsePartial(s); | ||
| return desugarTilde(partial); | ||
| } | ||
| if ("^" === ch) { | ||
| advance(s); | ||
| const partial = yield* parsePartial(s); | ||
| return desugarCaret(partial); | ||
| } | ||
| const operator = parseOperator(s); | ||
| const partial = yield* parsePartial(s); | ||
| return desugarXRange(operator, partial); | ||
| }); | ||
| const atRangeEnd = (s)=>{ | ||
| if (atEnd(s)) return true; | ||
| const saved = s.pos; | ||
| let pos = saved; | ||
| while(pos < s.len && " " === s.input[pos])pos++; | ||
| if (pos + 1 < s.len && "|" === s.input[pos] && "|" === s.input[pos + 1]) return true; | ||
| return false; | ||
| }; | ||
| const parseRangeComparators = (s)=>Effect.gen(function*() { | ||
| skipSpaces(s); | ||
| const savedPos = s.pos; | ||
| const tryHyphen = yield* Effect.either(Effect.gen(function*() { | ||
| const lower = yield* parsePartial(s); | ||
| if (!isHyphenRange(s)) return yield* Effect.fail(failRange(s)); | ||
| advance(s); | ||
| advance(s); | ||
| advance(s); | ||
| const upper = yield* parsePartial(s); | ||
| return desugarHyphen(lower, upper); | ||
| })); | ||
| if ("Right" === tryHyphen._tag) return tryHyphen.right; | ||
| s.pos = savedPos; | ||
| const comparators = []; | ||
| const first = yield* parseSimple(s); | ||
| for (const c of first)comparators.push(c); | ||
| while(!atRangeEnd(s)){ | ||
| if (" " !== peek(s)) break; | ||
| skipSpaces(s); | ||
| if (atRangeEnd(s)) break; | ||
| const next = yield* parseSimple(s); | ||
| for (const c of next)comparators.push(c); | ||
| } | ||
| return comparators; | ||
| }); | ||
| const parseRangeSet = (raw)=>Effect.gen(function*() { | ||
| const trimmed = raw.trim(); | ||
| if (0 === trimmed.length) return new Range({ | ||
| sets: [ | ||
| desugarXRange(null, { | ||
| major: null, | ||
| minor: null, | ||
| patch: null, | ||
| prerelease: [], | ||
| build: [] | ||
| }) | ||
| ] | ||
| }); | ||
| const s = { | ||
| input: trimmed, | ||
| pos: 0, | ||
| len: trimmed.length | ||
| }; | ||
| const sets = []; | ||
| const first = yield* parseRangeComparators(s); | ||
| sets.push(first); | ||
| while(!atEnd(s))if (isOrSeparator(s)) { | ||
| consumeOrSeparator(s); | ||
| const next = yield* parseRangeComparators(s); | ||
| sets.push(next); | ||
| } else break; | ||
| if (!atEnd(s)) return yield* Effect.fail(failRange(s)); | ||
| return new Range({ | ||
| sets | ||
| }); | ||
| }); | ||
| const parseSingleComparator = (raw)=>Effect.gen(function*() { | ||
| const trimmed = raw.trim(); | ||
| if (0 === trimmed.length) return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: raw, | ||
| position: 0 | ||
| })); | ||
| const s = { | ||
| input: trimmed, | ||
| pos: 0, | ||
| len: trimmed.length | ||
| }; | ||
| const operator = parseOperator(s); | ||
| const ch = peek(s); | ||
| if (">" === ch || "<" === ch || "=" === ch) return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| const major = yield* parseNumericIdentifierRange(s).pipe(Effect.mapError(()=>new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| if ("." !== peek(s)) return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| advance(s); | ||
| const minor = yield* parseNumericIdentifierRange(s).pipe(Effect.mapError(()=>new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| if ("." !== peek(s)) return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| advance(s); | ||
| const patch = yield* parseNumericIdentifierRange(s).pipe(Effect.mapError(()=>new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| let prerelease = []; | ||
| if (!atEnd(s) && "-" === peek(s)) { | ||
| advance(s); | ||
| prerelease = yield* parsePrereleaseRange(s).pipe(Effect.mapError(()=>new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| } | ||
| let build = []; | ||
| if (!atEnd(s) && "+" === peek(s)) { | ||
| advance(s); | ||
| build = yield* parseBuildRange(s).pipe(Effect.mapError(()=>new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| }))); | ||
| } | ||
| if (!atEnd(s)) return yield* Effect.fail(new InvalidComparatorError({ | ||
| input: trimmed, | ||
| position: s.pos | ||
| })); | ||
| const version = new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease, | ||
| build | ||
| }); | ||
| return new Comparator({ | ||
| operator: operator ?? "=", | ||
| version | ||
| }); | ||
| }); | ||
| const SemVerOrder = Order.make((a, b)=>a.compare(b)); | ||
| const SemVerOrderWithBuild = Order.make((a, b)=>{ | ||
| const base = a.compare(b); | ||
| if (0 !== base) return base; | ||
| const aHasBuild = a.build.length > 0; | ||
| const bHasBuild = b.build.length > 0; | ||
| if (!aHasBuild && bHasBuild) return -1; | ||
| if (aHasBuild && !bHasBuild) return 1; | ||
| const len = Math.min(a.build.length, b.build.length); | ||
| for(let i = 0; i < len; i++){ | ||
| if (a.build[i] < b.build[i]) return -1; | ||
| if (a.build[i] > b.build[i]) return 1; | ||
| } | ||
| if (a.build.length !== b.build.length) return a.build.length < b.build.length ? -1 : 1; | ||
| return 0; | ||
| import { EmptyCacheError, EmptyCacheErrorBase } from "./errors/EmptyCacheError.js"; | ||
| import { InvalidBumpError, InvalidBumpErrorBase } from "./errors/InvalidBumpError.js"; | ||
| import { InvalidComparatorError, InvalidComparatorErrorBase } from "./errors/InvalidComparatorError.js"; | ||
| import { InvalidPrereleaseError, InvalidPrereleaseErrorBase } from "./errors/InvalidPrereleaseError.js"; | ||
| import { InvalidRangeError, InvalidRangeErrorBase } from "./errors/InvalidRangeError.js"; | ||
| import { InvalidVersionError, InvalidVersionErrorBase } from "./errors/InvalidVersionError.js"; | ||
| import { UnsatisfiableConstraintError, UnsatisfiableConstraintErrorBase } from "./errors/UnsatisfiableConstraintError.js"; | ||
| import { UnsatisfiedRangeError, UnsatisfiedRangeErrorBase } from "./errors/UnsatisfiedRangeError.js"; | ||
| import { VersionFetchError, VersionFetchErrorBase } from "./errors/VersionFetchError.js"; | ||
| import { VersionNotFoundError, VersionNotFoundErrorBase } from "./errors/VersionNotFoundError.js"; | ||
| import { SemVerParser } from "./services/SemVerParser.js"; | ||
| import { SemVer, SemVerBump } from "./schemas/SemVer.js"; | ||
| import { Comparator } from "./schemas/Comparator.js"; | ||
| import { Range } from "./schemas/Range.js"; | ||
| import { parseSingleComparator, parseValidSemVer } from "./utils/grammar.js"; | ||
| import { SemVerOrder, SemVerOrderWithBuild } from "./utils/order.js"; | ||
| import { SemVerParserLive } from "./layers/SemVerParserLive.js"; | ||
| import { VersionCache } from "./services/VersionCache.js"; | ||
| import { VersionDiff } from "./schemas/VersionDiff.js"; | ||
| import { diff } from "./utils/diff.js"; | ||
| import { filter, maxSatisfying, minSatisfying, satisfies } from "./utils/matching.js"; | ||
| import { VersionCacheLive } from "./layers/VersionCacheLive.js"; | ||
| import { VersionFetcher } from "./services/VersionFetcher.js"; | ||
| import { equivalent, intersect, isSubset, simplify, union } from "./utils/algebra.js"; | ||
| import { bumpMajor, bumpMinor, bumpPatch, bumpPrerelease, bumpRelease } from "./utils/bump.js"; | ||
| import { compare, compareWithBuild, equal, gt, gte, isPrerelease, isStable, lt, lte, max, min, neq, rsort, sort, truncate } from "./utils/compare.js"; | ||
| import { parseRange } from "./utils/parseRange.js"; | ||
| import { prettyPrint } from "./utils/prettyPrint.js"; | ||
| //#region src/index.ts | ||
| SemVer.parse = parseValidSemVer; | ||
| SemVer.of = (major, minor, patch, prerelease = [], build = []) => new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: [...prerelease], | ||
| build: [...build] | ||
| }); | ||
| const operatorWeight = (op)=>{ | ||
| switch(op){ | ||
| case ">=": | ||
| return 0; | ||
| case ">": | ||
| return 1; | ||
| case "=": | ||
| return 2; | ||
| case "<": | ||
| return 3; | ||
| case "<=": | ||
| return 4; | ||
| default: | ||
| return 5; | ||
| } | ||
| }; | ||
| const sortComparators = (set)=>[ | ||
| ...set | ||
| ].sort((a, b)=>{ | ||
| const w = operatorWeight(a.operator) - operatorWeight(b.operator); | ||
| if (0 !== w) return w; | ||
| return SemVerOrder(a.version, b.version); | ||
| }); | ||
| const removeDuplicates = (set)=>{ | ||
| const seen = new Set(); | ||
| return set.filter((c)=>{ | ||
| const v = c.version; | ||
| const pre = v.prerelease.length > 0 ? `-${v.prerelease.join(".")}` : ""; | ||
| const key = `${c.operator}${v.major}.${v.minor}.${v.patch}${pre}`; | ||
| if (seen.has(key)) return false; | ||
| seen.add(key); | ||
| return true; | ||
| }); | ||
| }; | ||
| const normalizeComparatorSet = (set)=>sortComparators(removeDuplicates(set)); | ||
| const normalizeRange = (range)=>new Range({ | ||
| sets: range.sets.map((set)=>[ | ||
| ...normalizeComparatorSet(set) | ||
| ]) | ||
| }); | ||
| const SemVerParserLive = Layer.succeed(SemVerParser, SemVerParser.of({ | ||
| parseVersion: parseValidSemVer, | ||
| parseRange: (input)=>Effect.map(parseRangeSet(input), normalizeRange), | ||
| parseComparator: parseSingleComparator | ||
| })); | ||
| class VersionCache extends Context.Tag("semver-effect/VersionCache")() { | ||
| } | ||
| class VersionDiff extends Schema.TaggedClass()("VersionDiff", { | ||
| type: Schema.Literal("major", "minor", "patch", "prerelease", "build", "none"), | ||
| from: SemVer, | ||
| to: SemVer, | ||
| major: Schema.Number, | ||
| minor: Schema.Number, | ||
| patch: Schema.Number | ||
| }) { | ||
| toString() { | ||
| return `${this.type} (${this.from.toString()} → ${this.to.toString()})`; | ||
| } | ||
| toJSON() { | ||
| return { | ||
| _tag: "VersionDiff", | ||
| type: this.type, | ||
| from: this.from.toJSON(), | ||
| to: this.to.toJSON(), | ||
| major: this.major, | ||
| minor: this.minor, | ||
| patch: this.patch | ||
| }; | ||
| } | ||
| } | ||
| const arraysEqual = (a, b)=>a.length === b.length && a.every((v, i)=>v === b[i]); | ||
| const classifyDiff = (a, b)=>{ | ||
| if (a.major !== b.major) return "major"; | ||
| if (a.minor !== b.minor) return "minor"; | ||
| if (a.patch !== b.patch) return "patch"; | ||
| if (!arraysEqual(a.prerelease, b.prerelease)) return "prerelease"; | ||
| if (!arraysEqual(a.build, b.build)) return "build"; | ||
| return "none"; | ||
| }; | ||
| const diff = Function.dual(2, (a, b)=>new VersionDiff({ | ||
| type: classifyDiff(a, b), | ||
| from: a, | ||
| to: b, | ||
| major: b.major - a.major, | ||
| minor: b.minor - a.minor, | ||
| patch: b.patch - a.patch | ||
| })); | ||
| const satisfies = Function.dual(2, (version, range)=>range.test(version)); | ||
| const filter = Function.dual(2, (versions, range)=>range.filter(versions)); | ||
| const maxSatisfying = Function.dual(2, (versions, range)=>{ | ||
| let best = null; | ||
| for (const v of versions)if (range.test(v)) { | ||
| if (null === best || v.compare(best) > 0) best = v; | ||
| } | ||
| return null === best ? Option.none() : Option.some(best); | ||
| }); | ||
| const minSatisfying = Function.dual(2, (versions, range)=>{ | ||
| let best = null; | ||
| for (const v of versions)if (range.test(v)) { | ||
| if (null === best || v.compare(best) < 0) best = v; | ||
| } | ||
| return null === best ? Option.none() : Option.some(best); | ||
| }); | ||
| const toArray = (set)=>Array.from(SortedSet.values(set)); | ||
| const binarySearch = (arr, target)=>{ | ||
| let lo = 0; | ||
| let hi = arr.length - 1; | ||
| while(lo <= hi){ | ||
| const mid = lo + hi >>> 1; | ||
| const cmp = SemVerOrder(arr[mid], target); | ||
| if (0 === cmp) return mid; | ||
| if (cmp < 0) lo = mid + 1; | ||
| else hi = mid - 1; | ||
| } | ||
| return -1; | ||
| }; | ||
| const requireNonEmptySet = (set)=>{ | ||
| if (0 === SortedSet.size(set)) return Effect.fail(new EmptyCacheError()); | ||
| return Effect.succeed(toArray(set)); | ||
| }; | ||
| const VersionCacheLive = Layer.effect(VersionCache, Effect.gen(function*() { | ||
| const parser = yield* SemVerParser; | ||
| const ref = yield* Ref.make(SortedSet.empty(SemVerOrder)); | ||
| const resolveFromRef = (range)=>Effect.flatMap(Ref.get(ref), (set)=>{ | ||
| const arr = toArray(set); | ||
| for(let i = arr.length - 1; i >= 0; i--)if (satisfies(arr[i], range)) return Effect.succeed(arr[i]); | ||
| return Effect.fail(new UnsatisfiedRangeError({ | ||
| range, | ||
| available: arr | ||
| })); | ||
| }); | ||
| return VersionCache.of({ | ||
| load: (versions)=>Ref.set(ref, SortedSet.fromIterable(versions, SemVerOrder)), | ||
| add: (version)=>Ref.update(ref, SortedSet.add(version)), | ||
| remove: (version)=>Ref.update(ref, SortedSet.remove(version)), | ||
| get versions () { | ||
| return Effect.flatMap(Ref.get(ref), requireNonEmptySet); | ||
| }, | ||
| latest: ()=>Effect.flatMap(Ref.get(ref), (set)=>Effect.map(requireNonEmptySet(set), (arr)=>arr[arr.length - 1])), | ||
| oldest: ()=>Effect.flatMap(Ref.get(ref), (set)=>Effect.map(requireNonEmptySet(set), (arr)=>arr[0])), | ||
| resolve: (range)=>resolveFromRef(range), | ||
| resolveString: (input)=>Effect.flatMap(parser.parseRange(input), resolveFromRef), | ||
| filter: (range)=>Effect.flatMap(Ref.get(ref), (set)=>Effect.map(requireNonEmptySet(set), (arr)=>arr.filter((v)=>satisfies(v, range)))), | ||
| groupBy: (strategy)=>Effect.flatMap(Ref.get(ref), (set)=>Effect.map(requireNonEmptySet(set), (arr)=>{ | ||
| const map = new Map(); | ||
| for (const ver of arr){ | ||
| let key; | ||
| switch(strategy){ | ||
| case "major": | ||
| key = `${ver.major}`; | ||
| break; | ||
| case "minor": | ||
| key = `${ver.major}.${ver.minor}`; | ||
| break; | ||
| case "patch": | ||
| key = `${ver.major}.${ver.minor}.${ver.patch}`; | ||
| break; | ||
| } | ||
| const existing = map.get(key); | ||
| map.set(key, existing ? [ | ||
| ...existing, | ||
| ver | ||
| ] : [ | ||
| ver | ||
| ]); | ||
| } | ||
| return map; | ||
| })), | ||
| latestByMajor: ()=>Effect.flatMap(Ref.get(ref), (set)=>Effect.map(requireNonEmptySet(set), (arr)=>{ | ||
| const map = new Map(); | ||
| for (const ver of arr)map.set(ver.major, ver); | ||
| return Array.from(map.values()); | ||
| })), | ||
| latestByMinor: ()=>Effect.flatMap(Ref.get(ref), (set)=>Effect.map(requireNonEmptySet(set), (arr)=>{ | ||
| const map = new Map(); | ||
| for (const ver of arr)map.set(`${ver.major}.${ver.minor}`, ver); | ||
| return Array.from(map.values()); | ||
| })), | ||
| diff: (a, b)=>Effect.flatMap(Ref.get(ref), (set)=>{ | ||
| if (!SortedSet.has(set, a)) return Effect.fail(new VersionNotFoundError({ | ||
| version: a | ||
| })); | ||
| if (!SortedSet.has(set, b)) return Effect.fail(new VersionNotFoundError({ | ||
| version: b | ||
| })); | ||
| return Effect.succeed(diff(a, b)); | ||
| }), | ||
| next: (version)=>Effect.flatMap(Ref.get(ref), (set)=>{ | ||
| if (!SortedSet.has(set, version)) return Effect.fail(new VersionNotFoundError({ | ||
| version | ||
| })); | ||
| const arr = toArray(set); | ||
| const idx = binarySearch(arr, version); | ||
| if (idx < arr.length - 1) return Effect.succeed(Option.some(arr[idx + 1])); | ||
| return Effect.succeed(Option.none()); | ||
| }), | ||
| prev: (version)=>Effect.flatMap(Ref.get(ref), (set)=>{ | ||
| if (!SortedSet.has(set, version)) return Effect.fail(new VersionNotFoundError({ | ||
| version | ||
| })); | ||
| const arr = toArray(set); | ||
| const idx = binarySearch(arr, version); | ||
| if (idx > 0) return Effect.succeed(Option.some(arr[idx - 1])); | ||
| return Effect.succeed(Option.none()); | ||
| }) | ||
| }); | ||
| })); | ||
| class VersionFetcher extends Context.Tag("semver-effect/VersionFetcher")() { | ||
| } | ||
| const makeRange = (sets)=>new Range({ | ||
| sets: sets.map((s)=>[ | ||
| ...s | ||
| ]) | ||
| }); | ||
| const isSetSatisfiable = (set)=>{ | ||
| const lowers = []; | ||
| const uppers = []; | ||
| const equals = []; | ||
| for (const c of set)if ("=" === c.operator) equals.push(c); | ||
| else if (">" === c.operator || ">=" === c.operator) lowers.push(c); | ||
| else uppers.push(c); | ||
| for (const eq of equals)for (const c of set){ | ||
| if (c === eq) continue; | ||
| const cmp = SemVerOrder(eq.version, c.version); | ||
| switch(c.operator){ | ||
| case ">": | ||
| if (cmp <= 0) return false; | ||
| break; | ||
| case ">=": | ||
| if (cmp < 0) return false; | ||
| break; | ||
| case "<": | ||
| if (cmp >= 0) return false; | ||
| break; | ||
| case "<=": | ||
| if (cmp > 0) return false; | ||
| break; | ||
| case "=": | ||
| if (0 !== cmp) return false; | ||
| break; | ||
| } | ||
| } | ||
| for (const lo of lowers)for (const hi of uppers){ | ||
| const cmp = SemVerOrder(lo.version, hi.version); | ||
| if (">=" === lo.operator && "<" === hi.operator) { | ||
| if (cmp >= 0) return false; | ||
| } else if (">=" === lo.operator && "<=" === hi.operator) { | ||
| if (cmp > 0) return false; | ||
| } else if (">" === lo.operator && "<" === hi.operator) { | ||
| if (cmp >= 0) return false; | ||
| } else if (">" === lo.operator && "<=" === hi.operator) { | ||
| if (cmp >= 0) return false; | ||
| } | ||
| } | ||
| return true; | ||
| }; | ||
| const union = Function.dual(2, (a, b)=>makeRange([ | ||
| ...a.sets, | ||
| ...b.sets | ||
| ])); | ||
| const intersect = Function.dual(2, (a, b)=>{ | ||
| const candidates = []; | ||
| for (const setA of a.sets)for (const setB of b.sets){ | ||
| const merged = [ | ||
| ...setA, | ||
| ...setB | ||
| ]; | ||
| if (isSetSatisfiable(merged)) candidates.push(merged); | ||
| } | ||
| if (0 === candidates.length) return Effect.fail(new UnsatisfiableConstraintError({ | ||
| constraints: [ | ||
| a, | ||
| b | ||
| ] | ||
| })); | ||
| return Effect.succeed(makeRange(candidates)); | ||
| }); | ||
| const isComparatorImplied = (set, comp)=>{ | ||
| for (const s of set){ | ||
| const cmp = SemVerOrder(s.version, comp.version); | ||
| switch(comp.operator){ | ||
| case ">=": | ||
| if (">=" === s.operator && cmp >= 0 || ">" === s.operator && cmp >= 0) return true; | ||
| if ("=" === s.operator && cmp >= 0) return true; | ||
| break; | ||
| case ">": | ||
| if (">" === s.operator && cmp >= 0) return true; | ||
| if (">=" === s.operator && cmp > 0) return true; | ||
| if ("=" === s.operator && cmp > 0) return true; | ||
| break; | ||
| case "<=": | ||
| if ("<=" === s.operator && cmp <= 0 || "<" === s.operator && cmp <= 0) return true; | ||
| if ("=" === s.operator && cmp <= 0) return true; | ||
| break; | ||
| case "<": | ||
| if ("<" === s.operator && cmp <= 0) return true; | ||
| if ("<=" === s.operator && cmp < 0) return true; | ||
| if ("=" === s.operator && cmp < 0) return true; | ||
| break; | ||
| case "=": | ||
| if ("=" === s.operator && 0 === cmp) return true; | ||
| break; | ||
| } | ||
| } | ||
| return false; | ||
| }; | ||
| const isComparatorSetSubset = (sub, sup)=>{ | ||
| for (const supComp of sup)if (!isComparatorImplied(sub, supComp)) return false; | ||
| return true; | ||
| }; | ||
| const isSubset = Function.dual(2, (sub, sup)=>{ | ||
| for (const subSet of sub.sets){ | ||
| const contained = sup.sets.some((supSet)=>isComparatorSetSubset(subSet, supSet)); | ||
| if (!contained) return false; | ||
| } | ||
| return true; | ||
| }); | ||
| const equivalent = Function.dual(2, (a, b)=>isSubset(a, b) && isSubset(b, a)); | ||
| const simplify = (range)=>{ | ||
| const sets = range.sets.filter((set, i)=>!range.sets.some((other, j)=>i !== j && isComparatorSetSubset(set, other))); | ||
| if (0 === sets.length) return range; | ||
| return makeRange(sets); | ||
| }; | ||
| const bumpMajor = (v)=>v.bump.major(); | ||
| const bumpMinor = (v)=>v.bump.minor(); | ||
| const bumpPatch = (v)=>v.bump.patch(); | ||
| const bumpPrerelease = (v, id)=>v.bump.prerelease(id); | ||
| const bumpRelease = (v)=>v.bump.release(); | ||
| const compare = Function.dual(2, (self, that)=>SemVerOrder(self, that)); | ||
| const equal = Function.dual(2, (self, that)=>Equal.equals(self, that)); | ||
| const gt = Function.dual(2, (self, that)=>1 === SemVerOrder(self, that)); | ||
| const gte = Function.dual(2, (self, that)=>SemVerOrder(self, that) >= 0); | ||
| const lt = Function.dual(2, (self, that)=>-1 === SemVerOrder(self, that)); | ||
| const lte = Function.dual(2, (self, that)=>SemVerOrder(self, that) <= 0); | ||
| const neq = Function.dual(2, (self, that)=>!Equal.equals(self, that)); | ||
| const isPrerelease = (v)=>v.prerelease.length > 0; | ||
| const isStable = (v)=>0 === v.prerelease.length; | ||
| const truncate = Function.dual(2, (v, level)=>"prerelease" === level ? new SemVer({ | ||
| major: v.major, | ||
| minor: v.minor, | ||
| patch: v.patch, | ||
| prerelease: [], | ||
| build: [] | ||
| }) : new SemVer({ | ||
| major: v.major, | ||
| minor: v.minor, | ||
| patch: v.patch, | ||
| prerelease: [ | ||
| ...v.prerelease | ||
| ], | ||
| build: [] | ||
| })); | ||
| const sort = (versions)=>[ | ||
| ...versions | ||
| ].sort(SemVerOrder); | ||
| const rsort = (versions)=>[ | ||
| ...versions | ||
| ].sort((a, b)=>SemVerOrder(b, a)); | ||
| const max = (versions)=>{ | ||
| if (0 === versions.length) return Option.none(); | ||
| let result = versions[0]; | ||
| for(let i = 1; i < versions.length; i++)if (1 === SemVerOrder(versions[i], result)) result = versions[i]; | ||
| return Option.some(result); | ||
| }; | ||
| const min = (versions)=>{ | ||
| if (0 === versions.length) return Option.none(); | ||
| let result = versions[0]; | ||
| for(let i = 1; i < versions.length; i++)if (-1 === SemVerOrder(versions[i], result)) result = versions[i]; | ||
| return Option.some(result); | ||
| }; | ||
| const compareWithBuild = Function.dual(2, (self, that)=>SemVerOrderWithBuild(self, that)); | ||
| const parseRange = (input)=>Effect.map(parseRangeSet(input), normalizeRange); | ||
| const matcher = Match.type().pipe(Match.tag("SemVer", (sv)=>sv.toString()), Match.tag("Comparator", (c)=>c.toString()), Match.tag("Range", (r)=>r.toString()), Match.tag("VersionDiff", (d)=>d.toString()), Match.exhaustive); | ||
| const prettyPrint = (value)=>matcher(value); | ||
| SemVer.parse = parseValidSemVer; | ||
| SemVer.of = (major, minor, patch, prerelease = [], build = [])=>new SemVer({ | ||
| major, | ||
| minor, | ||
| patch, | ||
| prerelease: [ | ||
| ...prerelease | ||
| ], | ||
| build: [ | ||
| ...build | ||
| ] | ||
| }); | ||
| SemVer.compare = compare; | ||
@@ -1286,2 +57,4 @@ SemVer.equal = equal; | ||
| Range.minSatisfying = minSatisfying; | ||
| export { Comparator, EmptyCacheError, EmptyCacheErrorBase, InvalidBumpError, InvalidBumpErrorBase, InvalidComparatorError, InvalidComparatorErrorBase, InvalidPrereleaseError, InvalidPrereleaseErrorBase, InvalidRangeError, InvalidRangeErrorBase, InvalidVersionError, InvalidVersionErrorBase, Range, SemVer, SemVerBump, SemVerOrder, SemVerOrderWithBuild, SemVerParser, SemVerParserLive, UnsatisfiableConstraintError, UnsatisfiableConstraintErrorBase, UnsatisfiedRangeError, UnsatisfiedRangeErrorBase, VersionCache, VersionCacheLive, VersionDiff, VersionFetchError, VersionFetchErrorBase, VersionFetcher, VersionNotFoundError, VersionNotFoundErrorBase, bumpMajor, bumpMinor, bumpPatch, bumpPrerelease, bumpRelease, compare, compareWithBuild, diff, equal, equivalent, filter, gt, gte, intersect, isPrerelease, isStable, isSubset, lt, lte, max, maxSatisfying, min, minSatisfying, neq, parseRange, parseSingleComparator, parseValidSemVer, prettyPrint, rsort, satisfies, simplify, sort, truncate, union }; | ||
| //#endregion | ||
| export { Comparator, EmptyCacheError, EmptyCacheErrorBase, InvalidBumpError, InvalidBumpErrorBase, InvalidComparatorError, InvalidComparatorErrorBase, InvalidPrereleaseError, InvalidPrereleaseErrorBase, InvalidRangeError, InvalidRangeErrorBase, InvalidVersionError, InvalidVersionErrorBase, Range, SemVer, SemVerBump, SemVerOrder, SemVerOrderWithBuild, SemVerParser, SemVerParserLive, UnsatisfiableConstraintError, UnsatisfiableConstraintErrorBase, UnsatisfiedRangeError, UnsatisfiedRangeErrorBase, VersionCache, VersionCacheLive, VersionDiff, VersionFetchError, VersionFetchErrorBase, VersionFetcher, VersionNotFoundError, VersionNotFoundErrorBase, bumpMajor, bumpMinor, bumpPatch, bumpPrerelease, bumpRelease, compare, compareWithBuild, diff, equal, equivalent, filter, gt, gte, intersect, isPrerelease, isStable, isSubset, lt, lte, max, maxSatisfying, min, minSatisfying, neq, parseRange, parseSingleComparator, parseValidSemVer, prettyPrint, rsort, satisfies, simplify, sort, truncate, union }; |
+48
-55
| { | ||
| "name": "semver-effect", | ||
| "version": "0.2.1", | ||
| "private": false, | ||
| "description": "Strict SemVer 2.0.0 implementation built on Effect, providing typed parsing, range algebra and version cache services.", | ||
| "keywords": [ | ||
| "semver", | ||
| "semantic-versioning", | ||
| "version", | ||
| "versioning", | ||
| "semver-2", | ||
| "effect", | ||
| "effect-ts", | ||
| "typed-errors", | ||
| "range", | ||
| "comparator", | ||
| "version-cache", | ||
| "parsing", | ||
| "range-algebra" | ||
| ], | ||
| "homepage": "https://github.com/spencerbeggs/semver-effect#readme", | ||
| "bugs": { | ||
| "url": "https://github.com/spencerbeggs/semver-effect/issues" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/spencerbeggs/semver-effect.git" | ||
| }, | ||
| "license": "MIT", | ||
| "author": { | ||
| "name": "C. Spencer Beggs", | ||
| "email": "spencer@beggs.codes", | ||
| "url": "https://spencerbeg.gs" | ||
| }, | ||
| "sideEffects": false, | ||
| "type": "module", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./index.d.ts", | ||
| "import": "./index.js" | ||
| } | ||
| }, | ||
| "peerDependencies": { | ||
| "effect": ">=3.21.0" | ||
| }, | ||
| "files": [ | ||
| "!semver-effect.api.json", | ||
| "!tsconfig.json", | ||
| "!tsdoc.json", | ||
| "LICENSE", | ||
| "README.md", | ||
| "index.d.ts", | ||
| "index.js", | ||
| "package.json", | ||
| "tsdoc-metadata.json" | ||
| ] | ||
| "name": "semver-effect", | ||
| "version": "0.3.0", | ||
| "private": false, | ||
| "description": "Strict SemVer 2.0.0 implementation built on Effect, providing typed parsing, range algebra and version cache services.", | ||
| "keywords": [ | ||
| "semver", | ||
| "semantic-versioning", | ||
| "version", | ||
| "versioning", | ||
| "semver-2", | ||
| "effect", | ||
| "effect-ts", | ||
| "typed-errors", | ||
| "range", | ||
| "comparator", | ||
| "version-cache", | ||
| "parsing", | ||
| "range-algebra" | ||
| ], | ||
| "homepage": "https://github.com/spencerbeggs/semver-effect#readme", | ||
| "bugs": { | ||
| "url": "https://github.com/spencerbeggs/semver-effect/issues" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/spencerbeggs/semver-effect.git" | ||
| }, | ||
| "license": "MIT", | ||
| "author": { | ||
| "name": "C. Spencer Beggs", | ||
| "email": "spencer@beggs.codes", | ||
| "url": "https://spencerbeg.gs" | ||
| }, | ||
| "sideEffects": false, | ||
| "type": "module", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./index.d.ts", | ||
| "import": "./index.js" | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "peerDependencies": { | ||
| "effect": "^3.21.0" | ||
| }, | ||
| "engines": { | ||
| "node": ">=24.11.0" | ||
| } | ||
| } |
@@ -8,5 +8,5 @@ // This file is read by tools that parse documentation comments conforming to the TSDoc standard. | ||
| "packageName": "@microsoft/api-extractor", | ||
| "packageVersion": "7.57.7" | ||
| "packageVersion": "7.58.9" | ||
| } | ||
| ] | ||
| } |
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.
133791
29.89%36
500%3654
38.41%2
100%