From bce557cc2dc767628bed6aac87301a1be7c5431b Mon Sep 17 00:00:00 2001 From: rxliuli Date: Tue, 4 Nov 2025 05:03:50 +0800 Subject: init commit --- .../@amp/runtime-detect/dist/extensions/compare.js | 83 +++++ .../@amp/runtime-detect/dist/extensions/flags.js | 105 ++++++ .../node_modules/@amp/runtime-detect/dist/rules.js | 22 ++ .../@amp/runtime-detect/dist/user-agent.js | 392 +++++++++++++++++++++ .../@amp/runtime-detect/dist/version.js | 99 ++++++ 5 files changed, 701 insertions(+) create mode 100644 shared/utils/node_modules/@amp/runtime-detect/dist/extensions/compare.js create mode 100644 shared/utils/node_modules/@amp/runtime-detect/dist/extensions/flags.js create mode 100644 shared/utils/node_modules/@amp/runtime-detect/dist/rules.js create mode 100644 shared/utils/node_modules/@amp/runtime-detect/dist/user-agent.js create mode 100644 shared/utils/node_modules/@amp/runtime-detect/dist/version.js (limited to 'shared/utils/node_modules/@amp') diff --git a/shared/utils/node_modules/@amp/runtime-detect/dist/extensions/compare.js b/shared/utils/node_modules/@amp/runtime-detect/dist/extensions/compare.js new file mode 100644 index 0000000..200cdfb --- /dev/null +++ b/shared/utils/node_modules/@amp/runtime-detect/dist/extensions/compare.js @@ -0,0 +1,83 @@ +import { eq, gt, gte, lt, lte } from '../version.js'; + +function _define_property(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} +function _object_spread(target) { + for(var i = 1; i < arguments.length; i++){ + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + if (typeof Object.getOwnPropertySymbols === "function") { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + ownKeys.forEach(function(key) { + _define_property(target, key, source[key]); + }); + } + return target; +} +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) { + symbols = symbols.filter(function(sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + } + keys.push.apply(keys, symbols); + } + return keys; +} +function _object_spread_props(target, source) { + source = source != null ? source : {}; + if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function(key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + return target; +} +function compareExtension(descriptor) { + function makeComparable(data) { + var _data_major, _data_minor, _data_patch; + const version = { + major: (_data_major = data.major) !== null && _data_major !== void 0 ? _data_major : 0, + minor: (_data_minor = data.minor) !== null && _data_minor !== void 0 ? _data_minor : 0, + patch: (_data_patch = data.patch) !== null && _data_patch !== void 0 ? _data_patch : 0 + }; + return _object_spread_props(_object_spread({}, data), { + eq: (value)=>eq(version, value), + gt: (value)=>gt(version, value), + gte: (value)=>gte(version, value), + lt: (value)=>lt(version, value), + lte: (value)=>lte(version, value), + is: (value)=>data.name === value || data.variant === value + }); + } + return _object_spread_props(_object_spread({}, descriptor), { + extensions: [ + ...descriptor.extensions, + 'compare' + ], + browser: makeComparable(descriptor.browser), + engine: makeComparable(descriptor.engine), + os: makeComparable(descriptor.os) + }); +} + +export { compareExtension }; diff --git a/shared/utils/node_modules/@amp/runtime-detect/dist/extensions/flags.js b/shared/utils/node_modules/@amp/runtime-detect/dist/extensions/flags.js new file mode 100644 index 0000000..04980ee --- /dev/null +++ b/shared/utils/node_modules/@amp/runtime-detect/dist/extensions/flags.js @@ -0,0 +1,105 @@ +function _define_property(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} +function _object_spread(target) { + for(var i = 1; i < arguments.length; i++){ + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + if (typeof Object.getOwnPropertySymbols === "function") { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + ownKeys.forEach(function(key) { + _define_property(target, key, source[key]); + }); + } + return target; +} +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) { + symbols = symbols.filter(function(sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + } + keys.push.apply(keys, symbols); + } + return keys; +} +function _object_spread_props(target, source) { + source = source != null ? source : {}; + if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function(key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + return target; +} +function flagsExtension(descriptor) { + let osName = descriptor.os.name; + const isAndroid = descriptor.os.name === 'android'; + let isMacOS = descriptor.os.name === 'macos'; + let isIOS = descriptor.os.name === 'ios'; + var _descriptor_navigator_maxTouchPoints; + /** + * Newer iPads will identify as macOS in the UserAgent string but can still be differentiated by + * inspecting `maxTouchPoints`. The macOS and iOS values need to be reset when detected. + */ const isIPadOS = osName === 'ipados' || isIOS && /ipad/i.test(descriptor.ua) || isMacOS && ((_descriptor_navigator_maxTouchPoints = descriptor.navigator.maxTouchPoints) !== null && _descriptor_navigator_maxTouchPoints !== void 0 ? _descriptor_navigator_maxTouchPoints : 0) >= 2; + if (isIPadOS) { + osName = 'ipados'; + isIOS = false; + isMacOS = false; + } + const browser = _object_spread_props(_object_spread({}, descriptor.browser), { + isUnknown: descriptor.browser.name === 'unknown', + isSafari: descriptor.browser.name === 'safari', + isChrome: descriptor.browser.name === 'chrome', + isFirefox: descriptor.browser.name === 'firefox', + isEdge: descriptor.browser.name === 'edge', + isWebView: descriptor.browser.name === 'webview', + isOther: descriptor.browser.name === 'other', + isMobile: descriptor.browser.mobile || isIOS || isIPadOS || isAndroid || false + }); + const engine = _object_spread_props(_object_spread({}, descriptor.engine), { + isUnknown: descriptor.engine.name === 'unknown', + isWebKit: descriptor.engine.name === 'webkit', + isBlink: descriptor.engine.name === 'blink', + isGecko: descriptor.engine.name === 'gecko' + }); + const os = _object_spread_props(_object_spread({}, descriptor.os), { + name: osName, + isUnknown: descriptor.os.name === 'unknown', + isLinux: descriptor.os.name === 'linux', + isWindows: descriptor.os.name === 'windows', + isMacOS, + isAndroid, + isIOS, + isIPadOS + }); + return _object_spread_props(_object_spread({}, descriptor), { + extensions: [ + ...descriptor.extensions, + 'flags' + ], + browser, + os, + engine + }); +} + +export { flagsExtension }; diff --git a/shared/utils/node_modules/@amp/runtime-detect/dist/rules.js b/shared/utils/node_modules/@amp/runtime-detect/dist/rules.js new file mode 100644 index 0000000..5ffb91f --- /dev/null +++ b/shared/utils/node_modules/@amp/runtime-detect/dist/rules.js @@ -0,0 +1,22 @@ +function applyRules(rules, navigator, data) { + const { userAgent } = navigator !== null && navigator !== void 0 ? navigator : {}; + if (typeof userAgent !== 'string' || userAgent.trim() === '') { + return data; + } + for (const rule of rules){ + const patterns = rule.slice(0, -1); + const parser = rule[rule.length - 1]; + let match = null; + for (const pattern of patterns){ + match = userAgent.match(pattern); + if (match !== null) { + Object.assign(data, parser(match, navigator, data)); + break; + } + } + if (match !== null) break; + } + return data; +} + +export { applyRules }; diff --git a/shared/utils/node_modules/@amp/runtime-detect/dist/user-agent.js b/shared/utils/node_modules/@amp/runtime-detect/dist/user-agent.js new file mode 100644 index 0000000..f936336 --- /dev/null +++ b/shared/utils/node_modules/@amp/runtime-detect/dist/user-agent.js @@ -0,0 +1,392 @@ +import { parseVersion } from './version.js'; +import { applyRules } from './rules.js'; + +function _define_property(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} +function _object_spread(target) { + for(var i = 1; i < arguments.length; i++){ + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + if (typeof Object.getOwnPropertySymbols === "function") { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + ownKeys.forEach(function(key) { + _define_property(target, key, source[key]); + }); + } + return target; +} +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) { + symbols = symbols.filter(function(sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + } + keys.push.apply(keys, symbols); + } + return keys; +} +function _object_spread_props(target, source) { + source = source != null ? source : {}; + if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function(key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + return target; +} +var _match_, _match_1; +const RULES = { + // BROWSER ========================================================================== + browser: [ + // WEBVIEW ------------------------------------------------------------------------ + // iTunes/Music.app/TV.app + [ + /^(itunes|music|tv)\/([\w.]+)\s/i, + (match)=>_object_spread_props(_object_spread({ + name: 'webview', + variant: match[1].trim().toLowerCase().replace(/(music|tv)/i, '$1.app') + }, parseVersion(match[2])), { + mobile: false + }) + ], + // Facebook + [ + /(?:(?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w.]+);)/i, + (match)=>_object_spread({ + name: 'webview', + variant: 'facebook', + mobile: true + }, parseVersion(match[1])) + ], + // Instagram / SnapChat + [ + /(instagram|snapchat)[/ ]([-\w.]+)/i, + (match)=>_object_spread({ + name: 'webview', + variant: match[1].trim().toLowerCase(), + mobile: true + }, parseVersion(match[2])) + ], + // TikTok + [ + /musical_ly(?:.+app_?version\/|_)([\w.]+)/i, + (match)=>_object_spread({ + name: 'webview', + variant: 'tiktok', + mobile: true + }, parseVersion(match[1])) + ], + // Twitter + [ + /twitter/i, + ()=>({ + name: 'webview', + variant: 'twitter', + mobile: true + }) + ], + // Chrome WebView + [ + / wv\).+?(?:version|chrome)\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'webview', + mobile: true + }, parseVersion(match[1])) + ], + // ELECTRON ----------------------------------------------------------------------- + [ + /electron\/([\w.]+) safari/i, + (match)=>_object_spread({ + name: 'electron', + mobile: false + }, parseVersion(match[1])) + ], + // OTHER -------------------------------------------------------------------------- + [ + /tesla\/(.*?(20\d\d\.([-\w.])+))/i, + (match)=>_object_spread_props(_object_spread({ + name: 'other', + variant: 'tesla', + mobile: false + }, parseVersion(match[2])), { + version: match[1] + }) + ], + [ + /(samsung|huawei)browser\/([-\w.]+)/i, + (match)=>_object_spread({ + name: 'other', + variant: match[1].trim().toLowerCase().replace(/browser/i, ''), + mobile: true + }, parseVersion(match[2])) + ], + [ + /yabrowser\/([-\w.]+)/i, + (match)=>_object_spread({ + name: 'other', + variant: 'yandex', + mobile: false + }, parseVersion(match[1])) + ], + [ + /(brave|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w.]+)/i, + (match, { userAgent })=>_object_spread({ + name: 'other', + variant: match[1].trim().toLowerCase(), + mobile: /mobile/i.test(userAgent) + }, parseVersion(match[2].replace(/-/g, '.'))) + ], + // EDGE / IE ---------------------------------------------------------------------- + [ + /edg(e|ios|a)?\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'edge', + mobile: /(edgios|edga)/i.test((_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : '') + }, parseVersion(match[2])) + ], + [ + /trident.+rv[: ]([\w.]{1,9})\b.+like gecko/i, + (match)=>_object_spread({ + name: 'ie', + mobile: false + }, parseVersion(match[1])) + ], + // OPERA -------------------------------------------------------------------------- + [ + /opr\/([\w.]+)/i, + /opera mini\/([-\w.]+)/i, + /opera [mobiletab]{3,6}\b.+version\/([-\w.]+)/i, + /opera(?:.+version\/|[/ ]+)([\w.]+)/i, + (match)=>_object_spread({ + name: 'opera', + mobile: /mobile/i.test(match[0]) + }, parseVersion(match[1])) + ], + // CHROME ------------------------------------------------------------------------- + // Headless + [ + /headlesschrome(?:\/([\w.]+)| )/i, + (match)=>_object_spread({ + name: 'chrome', + variant: 'headless', + mobile: false + }, parseVersion(match[1])) + ], + // Chrome for iOS + [ + /\b(?:crmo|crios)\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'chrome', + mobile: true + }, parseVersion(match[1])) + ], + // Chrome + [ + /chrome(?: browser)?\/v?([\w.]+)( mobile)?/i, + (match)=>_object_spread({ + name: 'chrome', + mobile: /mobile/i.test((_match_1 = match[2]) !== null && _match_1 !== void 0 ? _match_1 : '') + }, parseVersion(match[1])) + ], + // FIREFOX ------------------------------------------------------------------------ + // Focus + [ + /\bfocus\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'firefox', + variant: 'focus', + mobile: true + }, parseVersion(match[1])) + ], + // Firefox for iOS + [ + /fxios\/([\w.-]+)/i, + /(?:mobile|tablet);.*(?:firefox)\/([\w.-]+)/i, + (match)=>_object_spread({ + name: 'firefox', + mobile: true + }, parseVersion(match[1])) + ], + // Firefox OSS versions + [ + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[/ ]?([\w.+]+)/i, + (match)=>_object_spread({ + name: 'firefox', + variant: match[1].trim().toLowerCase(), + mobile: false + }, parseVersion(match[2])) + ], + // Firefox + [ + /(?:firefox)\/([\w.]+)/i, + /(?:mozilla)\/([\w.]+) .+rv:.+gecko\/\d+/i, + (match)=>_object_spread({ + name: 'firefox', + mobile: false + }, parseVersion(match[1])) + ], + // SAFARI ------------------------------------------------------------------------- + [ + /version\/([\w.,]+) .*mobile(?:\/\w+ | ?)safari/i, + /version\/([\w.,]+) .*(safari)/i, + /webkit.+?(?:mobile ?safari|safari)(?:\/([\w.]+))/i, + (match)=>_object_spread({ + name: 'safari', + mobile: /mobile/i.test(match[0]) + }, parseVersion(match[1])) + ] + ], + // ENGINE --------------------------------------------------------------------------- + engine: [ + [ + /webkit\/(?:537\.36).+chrome\/(?!27)([\w.]+)/i, + (match)=>_object_spread({ + name: 'blink' + }, parseVersion(match[1])) + ], + [ + /windows.+ edge\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'blink' + }, parseVersion(match[1])) + ], + [ + /presto\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'presto' + }, parseVersion(match[2])) + ], + [ + /trident\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'trident' + }, parseVersion(match[1])) + ], + [ + /gecko\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'gecko' + }, parseVersion(match[1])) + ], + [ + /(khtml|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'other' + }, parseVersion(match[2])) + ], + [ + /webkit\/([\w.]+)/i, + (match)=>_object_spread({ + name: 'webkit' + }, parseVersion(match[1])) + ] + ], + // OS ------------------------------------------------------------------------------- + os: [ + // Windows + [ + /microsoft windows (vista|xp)/i, + /windows nt 6\.2; (arm)/i, + /windows (?:phone(?: os)?|mobile)[/ ]?([\d.\w ]*)/i, + /windows[/ ]?([ntce\d. ]+\w)(?!.+xbox)/i, + /(?:win(?=3|9|n)|win 9x )([nt\d.]+)/i, + (match)=>_object_spread({ + name: 'windows' + }, parseVersion(match[1])) + ], + // iOS (iPhone/iPad) + [ + /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, + /(?:ios;fbsv\/|iphone.+ios[/ ])([\d.]+)/i, + (match)=>_object_spread({ + name: 'ios' + }, parseVersion(match[1].replace(/_/g, '.'))) + ], + // macOS + [ + /mac(?:intosh;?)? os x ?([\d._]+)/i, + (match)=>_object_spread({ + name: 'macos' + }, parseVersion(match[1].replace(/_/g, '.'))) + ], + // ChromeOS + [ + /cros [\w]+(?:\)| ([\w.]+)\b)/i, + (match)=>_object_spread({ + name: 'chromeos' + }, parseVersion(match[1])) + ], + // Android + [ + /(?:android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-/ ]?([\w.]*)/i, + /droid ([\w.]+|[\d+])\b.+(android[- ]x86|harmonyos)/i, + (match)=>_object_spread({ + name: 'android' + }, parseVersion(match[1])) + ], + // Linux + [ + /linux/i, + ()=>({ + name: 'linux' + }) + ] + ] +}; +/** + * Extend a data structure by running a list of functions over it. + */ function applyExtensions(data, extensions) { + let result = data; + for (const extension of extensions){ + result = extension(result); + } + return result; +} +/** + * Parse the user agent string from the navigator object into a descriptor. + * + * @param navigator The Navigator object to parse + * @param options Parse options + * @returns The descriptor with optional extensions applied + */ function parseUserAgent(navigator, options) { + var _navigator, _options; + var _navigator_userAgent; + const descriptor = { + navigator: navigator, + ua: (_navigator_userAgent = (_navigator = navigator) === null || _navigator === void 0 ? void 0 : _navigator.userAgent) !== null && _navigator_userAgent !== void 0 ? _navigator_userAgent : '', + extensions: [], + browser: applyRules(RULES.browser, navigator, { + name: 'unknown', + mobile: false + }), + engine: applyRules(RULES.engine, navigator, { + name: 'unknown' + }), + os: applyRules(RULES.os, navigator, { + name: 'unknown' + }) + }; + var _options_extensions; + return applyExtensions(descriptor, (_options_extensions = (_options = options) === null || _options === void 0 ? void 0 : _options.extensions) !== null && _options_extensions !== void 0 ? _options_extensions : []); +} + +export { parseUserAgent }; diff --git a/shared/utils/node_modules/@amp/runtime-detect/dist/version.js b/shared/utils/node_modules/@amp/runtime-detect/dist/version.js new file mode 100644 index 0000000..eba858c --- /dev/null +++ b/shared/utils/node_modules/@amp/runtime-detect/dist/version.js @@ -0,0 +1,99 @@ +function parseVersion(input) { + const data = { + version: input.toLowerCase() + }; + const parts = input.toLowerCase().split('.').filter((p)=>!!p); + // Only parse single parts that are actual numbers. + // This check will prevent `parseInt` from pasing the leading chars if they are + // valid numbers. + if (parts.length <= 1 && !/^\d+$/.test(parts[0])) { + return data; + } + const major = parseInt(parts[0], 10); + const minor = parseInt(parts[1], 10); + const patch = parseInt(parts[2], 10); + // Only add converted versions if `major` part was valid + if (!Number.isNaN(major)) { + data.major = major; + if (!Number.isNaN(minor)) data.minor = minor; + if (!Number.isNaN(patch)) data.patch = patch; + } + return data; +} +/** + * Check if the given value is a complete Version struct. + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any +function isVersion(value) { + var _value, _value1; + return typeof ((_value = value) === null || _value === void 0 ? void 0 : _value.major) === 'number' && typeof ((_value1 = value) === null || _value1 === void 0 ? void 0 : _value1.minor) === 'number'; +} +/** + * Compare two version numbers together. + * + * NOTE: This only supports the first 3 segments (major, minor, patch) and does not + * do a full SemVer compare. + * + * @example + * ```javascript + * compareVersion('1.2.3', '1.2.4'); + * // => -1 + * ``` + */ function compareVersion(base, comp) { + let baseList = toNumbers(base); + let compList = toNumbers(comp); + // Right pad versions with zeros to make them equal length + const versionLength = Math.max(baseList.length, compList.length); + baseList = baseList.concat(Array(versionLength).fill(0)).slice(0, versionLength); + compList = compList.concat(Array(versionLength).fill(0)).slice(0, versionLength); + /** Constrain the given value to the output range of this function. */ const constrain = (value)=>{ + if (value <= -1) return -1; + else if (value >= 1) return 1; + else return 0; + }; + for(let index = 0; index < versionLength; index++){ + const aValue = baseList[index]; + const bValue = compList[index]; + if (aValue !== bValue) { + return constrain(aValue - bValue); + } + } + return 0; +} +function eq(base, comp) { + return compareVersion(base, comp) === 0; +} +function gt(base, comp) { + return compareVersion(base, comp) > 0; +} +function gte(base, comp) { + const result = compareVersion(base, comp); + return result > 0 || result === 0; +} +function lt(base, comp) { + return compareVersion(base, comp) < 0; +} +function lte(base, comp) { + const result = compareVersion(base, comp); + return result < 0 || result === 0; +} +function toNumbers(value) { + if (Array.isArray(value)) { + return value; + } else if (typeof value === 'number') { + return [ + value + ]; + } else if (typeof value === 'string') { + return toNumbers(parseVersion(value)); + } else { + const values = [ + value.major, + value.minor, + value.patch + ]; + const uidx = values.indexOf(undefined); + return uidx === -1 ? values : values.slice(0, uidx); + } +} + +export { compareVersion, eq, gt, gte, isVersion, lt, lte, parseVersion }; -- cgit v1.2.3