airtap-match-browsers
Advanced tools
+108
-107
@@ -8,3 +8,6 @@ 'use strict' | ||
| const names = require('browser-names') | ||
| const defaults = { version: 'latest' } | ||
| const prerelease = /[^\d.]/ | ||
| const numeric = /^\d+$/ | ||
@@ -24,3 +27,3 @@ module.exports = matchAll | ||
| for (const manifest of available) { | ||
| const name = lower(manifest.name) | ||
| const name = manifest.name | ||
@@ -34,23 +37,22 @@ if (groups.has(name)) { | ||
| // Presort versions and add aliases | ||
| for (const [name, group] of groups) { | ||
| group.sort((a, b) => cmpVersion(a.version, b.version)) | ||
| for (const w of wanted) { | ||
| const explicit = new Set() | ||
| for (const alias of names(name)) { | ||
| if (alias !== name) groups.set(alias, group) | ||
| // Match by name | ||
| let group = findName(groups, w.name) || [] | ||
| // Lazily sort by version | ||
| if (!group.sorted) { | ||
| group.sort((a, b) => cmpVersion(a.version, b.version)) | ||
| group.sorted = true | ||
| } | ||
| } | ||
| for (const w of wanted) { | ||
| const explicit = new Set() | ||
| // Match by other properties | ||
| const skip = ['name', 'version'] | ||
| group = group.filter(m => match(m, w, explicit, skip)) | ||
| // Match by name and version | ||
| let group = groups.get(w.name) || [] | ||
| // Match by version | ||
| w.version = lower(w.version) | ||
| group = filterVersions(group, w.version) | ||
| // Match by properties other than name and version | ||
| const skip = ['name', 'version'] | ||
| group = group.filter(m => match(m, w, explicit, skip)) | ||
| if (group.length === 0) { | ||
@@ -63,3 +65,3 @@ throw new Error('Zero matches for ' + JSON.stringify(w, null, 2)) | ||
| const a = group[i] | ||
| const alternatives = [a] | ||
| let winner = a | ||
@@ -70,3 +72,4 @@ for (let j = i + 1; j < group.length; j++) { | ||
| if (same(a, b, explicit)) { | ||
| alternatives.push(b) | ||
| // Last manifest wins (for no particular reason) | ||
| winner = b | ||
| group.splice(j--, 1) | ||
@@ -76,25 +79,2 @@ } | ||
| // Pick winner by preferredOver rules (or short of that, the last manifest) | ||
| let winner = alternatives[alternatives.length - 1] | ||
| let max = 0 | ||
| // TODO: optimize by merging logic into above loop | ||
| // TODO: find a simpler solution to deduplication overall | ||
| for (let x = 0; x < alternatives.length; x++) { | ||
| for (let y = x + 1; y < alternatives.length; y++) { | ||
| const weightX = preferredOver(alternatives[x], alternatives[y]) | ||
| const weightY = preferredOver(alternatives[y], alternatives[x]) | ||
| if (weightX > max) { | ||
| max = weightX | ||
| winner = alternatives[x] | ||
| } | ||
| if (weightY > max) { | ||
| max = weightY | ||
| winner = alternatives[y] | ||
| } | ||
| } | ||
| } | ||
| // Don't merge options into the manifest yet, so that we can | ||
@@ -111,2 +91,14 @@ // perform fast deduplication by object identity (below). We | ||
| function findName (groups, name) { | ||
| if (groups.has(name)) { | ||
| return groups.get(name) | ||
| } | ||
| for (const alias of names(name)) { | ||
| if (alias !== name && groups.has(alias)) { | ||
| return groups.get(alias) | ||
| } | ||
| } | ||
| } | ||
| function consolidate (matches) { | ||
@@ -150,3 +142,3 @@ for (let i = 0; i < matches.length; i++) { | ||
| if (typeof manifest.name !== 'string' || manifest.name === '') { | ||
| throw new TypeError('Browser "name" is required') | ||
| throw new TypeError('Manifest "name" is required') | ||
| } | ||
@@ -171,24 +163,2 @@ | ||
| function preferredOver (a, b) { | ||
| let weight = 0 | ||
| if (a.preferredOver) { | ||
| for (const k of Object.keys(a.preferredOver)) { | ||
| const values = a.preferredOver[k].map(lower) | ||
| const value = deep(b, k) | ||
| if (value == null) { | ||
| continue | ||
| } else if (values.includes(lower(value))) { | ||
| // A specific value has more weight than "any" | ||
| weight += 1e3 | ||
| } else if (values.includes('any')) { | ||
| weight += 1 | ||
| } | ||
| } | ||
| } | ||
| return weight | ||
| } | ||
| function lower (value) { | ||
@@ -202,4 +172,6 @@ return value != null ? String(value).toLowerCase() : '' | ||
| } else if (Array.isArray(wanted)) { | ||
| throw new Error('Array is not yet supported on ' + key) | ||
| // TODO: explode into multiple browsers, instead of this "oneof" behavior | ||
| return wanted.some(el => match(available, el, explicit, skip, key)) | ||
| // return wanted.some(el => match(available, el, explicit, skip, key)) | ||
| } else if (isObject(wanted)) { | ||
@@ -212,3 +184,3 @@ if (!isObject(available)) return false | ||
| if (!hasOwnProperty.call(wanted, k)) continue | ||
| if (fqk === 'options' || fqk === 'preferredOver') continue | ||
| if (fqk === 'options') continue | ||
| if (!match(available[k], wanted[k], explicit, skip, fqk)) return false | ||
@@ -247,45 +219,51 @@ } | ||
| function filterVersions (manifests, version) { | ||
| const [gte, lte] = range(version, manifests) | ||
| let start = 0 | ||
| let end = manifests.length | ||
| if (gte) { | ||
| while (start < end && cmpVersion(manifests[start].version, gte) < 0) { | ||
| start++ | ||
| } | ||
| if (!matchVersion(gte, manifests[start] && manifests[start].version)) { | ||
| throw new Error(`Version not found: ${gte}`) | ||
| } | ||
| if (manifests.length === 0) { | ||
| return manifests | ||
| } | ||
| if (lte) { | ||
| while (end > start && cmpVersion(manifests[end - 1].version, lte) > 0) { | ||
| end-- | ||
| } | ||
| const test = range(version, manifests) | ||
| const result = [] | ||
| if (!matchVersion(lte, manifests[end - 1] && manifests[end - 1].version)) { | ||
| throw new Error(`Version not found: ${lte}`) | ||
| for (const m of manifests) { | ||
| if (test(m.version)) { | ||
| result.push(m) | ||
| } else if (result.length) { | ||
| break | ||
| } | ||
| } | ||
| return manifests.slice(start, end) | ||
| return result | ||
| } | ||
| // Assumes manifests are sorted by version. | ||
| function range (version, manifests) { | ||
| const arr = version.split('..') | ||
| let gte | ||
| let lte | ||
| if (arr.length === 1) { | ||
| arr.push(arr[0]) | ||
| if (version.indexOf('..') === -1) { | ||
| gte = lte = resolve(version || 'latest') | ||
| } else { | ||
| const arr = version.split('..') | ||
| gte = resolve(arr[0] || 'oldest') | ||
| lte = resolve(arr[1] || 'latest') | ||
| } | ||
| return arr.map(function (v) { | ||
| if (!manifests.length) return | ||
| return function test (v) { | ||
| const c1 = cmpRange(v, gte, false) | ||
| if (c1 < 0) return false | ||
| const c2 = cmpRange(v, lte, true) | ||
| if (c2 > 0) return false | ||
| return true | ||
| } | ||
| function resolve (v) { | ||
| if (v === 'oldest') return manifests[0].version | ||
| if (v === 'latest') return latest(manifests, 0) | ||
| if (!isNaN(v) && v < 0) return latest(manifests, v * -1) | ||
| if (/^-\d+$/.test(v) && v < 0) return latest(manifests, v * -1) | ||
| return v | ||
| }) | ||
| } | ||
| } | ||
@@ -295,3 +273,3 @@ | ||
| for (let i = manifests.length - 1; i >= 0; i--) { | ||
| if (!isBeta(manifests[i].version) && !n--) { | ||
| if (!isPrerelease(manifests[i].version) && (!n-- || i === 0)) { | ||
| return manifests[i].version | ||
@@ -301,28 +279,51 @@ } | ||
| return manifests[0].version | ||
| // All are prereleases, return the last | ||
| return manifests[manifests.length - 1].version | ||
| } | ||
| function isBeta (version) { | ||
| return version && isNaN(version) | ||
| function isPrerelease (version) { | ||
| return !version || prerelease.test(version) | ||
| } | ||
| function matchVersion (wanted, available) { | ||
| if (!available) return false | ||
| if (isBeta(available)) return available === wanted | ||
| return available.startsWith(wanted) | ||
| function cmpVersion (a, b) { | ||
| return cmpRange(a, b, false) | ||
| } | ||
| function cmpVersion (a, b) { | ||
| if (a == null) return b == null ? 0 : 1 | ||
| if (b == null) return -1 | ||
| function cmpRange (a, b, prefixOnly) { | ||
| // Missing version behaves like last prerelease | ||
| if (!a) return !b ? 0 : 1 | ||
| if (!b) return -1 | ||
| if (isNaN(a)) return isNaN(b) ? a.localeCompare(b) : 1 | ||
| if (isNaN(b)) return -1 | ||
| const ap = isPrerelease(a) | ||
| const bp = isPrerelease(b) | ||
| return Number(a) - Number(b) | ||
| if (ap !== bp) return ap ? 1 : -1 | ||
| const av = a.split('.') | ||
| const bv = b.split('.') | ||
| for (let i = 0; i < Math.min(av.length, bv.length); i++) { | ||
| const cmp = cmpElement(av[i], bv[i]) | ||
| if (cmp > 0) return 1 | ||
| if (cmp < 0) return -1 | ||
| } | ||
| if (prefixOnly || av.length === bv.length) { | ||
| return 0 | ||
| } else { | ||
| return av.length > bv.length ? 1 : -1 | ||
| } | ||
| } | ||
| function cmpElement (a, b) { | ||
| if (numeric.test(a) && numeric.test(b)) { | ||
| return a - b | ||
| } else { | ||
| return a.localeCompare(b) | ||
| } | ||
| } | ||
| function isObject (o) { | ||
| return typeof o === 'object' && o !== null && !Array.isArray(o) | ||
| } |
+1
-1
| { | ||
| "name": "airtap-match-browsers", | ||
| "version": "0.0.2", | ||
| "version": "0.1.0", | ||
| "description": "Match browser manifests to a desired set of browsers", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+3
-3
| # airtap-match-browsers | ||
| > **Match [browser manifests](https://github.com/airtap/browser-manifest) to a desired set of browsers.** | ||
| > Intended to replace [`sauce-browsers`](https://github.com/lpinca/sauce-browsers) in conjunction with [`airtap-sauce-browsers`](https://github.com/airtap/sauce-browsers). | ||
| > Replaces [`sauce-browsers`](https://github.com/lpinca/sauce-browsers) in conjunction with [`airtap-sauce-browsers`](https://github.com/airtap/sauce-browsers). | ||
@@ -45,6 +45,6 @@ [](https://www.npmjs.org/package/airtap-match-browsers) | ||
| - A negative range in the form of `-<n>..latest`, for example `-1..latest` which means the last 2 numeric versions. | ||
| - A non-numeric version like "dev" and "beta". Such versions sort after numeric versions, so that `oldest..latest` excludes "dev" and `latest..dev` includes latest, "beta" and "dev". | ||
| - A prerelease version like "dev", "beta", "80.0a1". Such versions sort after numeric versions, so that `oldest..latest` excludes "dev" and `latest..dev` includes e.g. latest, "beta" and "dev". | ||
| - An array of versions. | ||
| If a version is not found (including the start and end of ranges) an error is thrown. | ||
| If a manifest doesn't have a `version`, it behaves like a prerelease. | ||
@@ -51,0 +51,0 @@ ### `platform` and any other (nested) property |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
0
-100%12249
-4.36%