Socket
Socket
Sign inDemoInstall

airtap-match-browsers

Package Overview
Dependencies
66
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.0 to 1.0.0

207

index.js

@@ -8,4 +8,4 @@ 'use strict'

const names = require('browser-names')
const isForkPr = require('is-fork-pr').isForkPr
const defaults = { version: 'latest' }
const prerelease = /[^\d.]/

@@ -17,43 +17,18 @@ const numeric = /^\d+$/

function matchAll (available, wanted) {
wanted = wanted.map(withDefaults)
wanted.sort((a, b) => a.name.localeCompare(b.name))
explode(wanted)
const matches = []
const groups = new Map()
const eqlOptions = { strict: true }
// Group by name for faster matching
for (const manifest of available) {
const name = manifest.name
for (const original of wanted) {
const w = normalize(original)
const explicit = new Set(['version'])
if (groups.has(name)) {
groups.get(name).push(manifest)
} else {
groups.set(name, [manifest])
}
}
// Match by properties other than version
let group = available.filter(m => match(m, w, explicit))
for (const w of wanted) {
const explicit = new Set()
// 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
}
// Match by other properties
const skip = ['name', 'version']
group = group.filter(m => match(m, w, explicit, skip))
// Match by version
w.version = lower(w.version)
group = filterVersions(group, w.version)
group.sort((a, b) => cmpVersion(a.version, b.version))
group = filterVersions(group, w.version || 'latest')
if (group.length === 0) {
throw new Error('Zero matches for ' + JSON.stringify(w, null, 2))
throw new NotFoundError(original)
}

@@ -69,3 +44,3 @@

if (same(a, b, explicit)) {
if (same(a, b, explicit, eqlOptions)) {
// Last manifest wins (for no particular reason)

@@ -80,26 +55,25 @@ winner = b

// assume that `available` itself doesn't contain duplicates.
matches.push({ manifest: winner, options: w.options })
matches.push({ manifest: winner, options: w.options || {} })
}
}
consolidate(matches)
consolidate(matches, eqlOptions)
return matches
}
function findName (groups, name) {
if (groups.has(name)) {
return groups.get(name)
}
function consolidate (matches, eqlOptions) {
const insecure = insecureEnv()
for (const alias of names(name)) {
if (alias !== name && groups.has(alias)) {
return groups.get(alias)
}
}
}
function consolidate (matches) {
for (let i = 0; i < matches.length; i++) {
const { manifest, options } = matches[i]
// Skip browsers that need secure environment variables and are therefore
// not available on pull requests from forks. Done here (after matching)
// so that the order of precedence between browsers is consistent in
// secure and insecure envs. Could reconsider that; needs a discussion.
if (insecure && manifest.wants && manifest.wants.secureEnv) {
matches.splice(i--, 1)
continue
}
// Add user-provided options to manifest

@@ -111,3 +85,3 @@ matches[i] = mergeDeep(manifest, { options })

if (matches[j].manifest === manifest &&
deepEqual(matches[j].options, options, { strict: true })) {
deepEqual(matches[j].options, options, eqlOptions)) {
matches.splice(j--, 1)

@@ -119,56 +93,21 @@ }

function explode (manifests) {
for (let i = 0; i < manifests.length; i++) {
const manifest = manifests[i]
for (const k of ['version']) {
if (Array.isArray(manifest[k])) {
manifests.splice(i--, 1, ...manifest[k].map(v => ({ ...manifest, [k]: v })))
break
}
}
}
}
function withDefaults (manifest) {
manifest = { ...manifest }
for (const k in defaults) {
manifest[k] = manifest[k] || defaults[k]
}
if (typeof manifest.name !== 'string' || manifest.name === '') {
throw new TypeError('Manifest "name" is required')
}
manifest.name = lower(manifest.name)
manifest.options = manifest.options || {}
function normalize (wanted) {
// For airtap < 4 compatibility
// TODO: consider adding a shorthand "device" property for ipad & iphone
if (manifest.name === 'iphone' || manifest.name === 'ipad') {
const device = manifest.name === 'iphone' ? 'iphone simulator' : 'ipad simulator'
const caps = manifest.capabilities = { ...manifest.capabilities }
if (wanted.name === 'iphone' || wanted.name === 'ipad') {
wanted = { ...wanted }
const device = wanted.name === 'iphone' ? 'iphone simulator' : 'ipad simulator'
const caps = wanted.capabilities = { ...wanted.capabilities }
const appium = caps.appium = { ...caps.appium }
manifest.name = 'ios_saf'
wanted.name = 'ios_saf'
appium.deviceName = appium.deviceName || device
}
return manifest
return wanted
}
function lower (value) {
return value != null ? String(value).toLowerCase() : ''
}
function match (available, wanted, explicit, skip, key) {
if (Array.isArray(available)) {
return available.some(el => match(el, wanted, explicit, skip, key))
} 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))
} else if (isObject(wanted)) {
function match (available, wanted, explicit, key) {
if (isObject(wanted)) {
if (!isObject(available)) return false

@@ -180,27 +119,37 @@

if (!hasOwnProperty.call(wanted, k)) continue
if (fqk === 'options') continue
if (!match(available[k], wanted[k], explicit, skip, fqk)) return false
if (fqk === 'options' || fqk === 'version') continue
explicit.add(fqk)
if (!match(available[k], wanted[k], explicit, fqk)) return false
}
return true
} else if (wanted === 'any') {
return true
} else {
explicit.add(key)
return skip.includes(key) || matchPrimitive(available, wanted)
return matchPrimitive(available, wanted, key)
}
}
function matchPrimitive (available, wanted) {
function matchPrimitive (available, wanted, key) {
if (typeof wanted === 'string') {
wanted = lower(wanted)
available = lower(available)
wanted = wanted != null ? String(wanted).toLowerCase() : ''
available = available != null ? String(available).toLowerCase() : ''
}
return available === wanted
if (available === wanted) {
return true
}
if (key === 'name') {
for (const alias of names(wanted)) {
if (available === alias) return true
}
}
return false
}
function same (a, b, explicit) {
function same (a, b, explicit, eqlOptions) {
for (const k of explicit) {
if (!deepEqual(deep(a, k), deep(b, k), { strict: true })) {
if (!deepEqual(deep(a, k), deep(b, k), eqlOptions)) {
return false

@@ -220,12 +169,4 @@ }

const test = range(version, manifests)
const result = []
const result = manifests.filter(m => test(m.version))
for (const m of manifests) {
if (test(m.version)) {
result.push(m)
} else if (result.length) {
break
}
}
return result

@@ -236,2 +177,10 @@ }

function range (version, manifests) {
if (Array.isArray(version)) {
const tests = version.map(v => range(v, manifests))
return function test (v) {
return tests.some(fn => fn(v))
}
}
let gte

@@ -322,4 +271,28 @@ let lte

function insecureEnv () {
if (process.env.AIRTAP_IS_SECURE_ENV) {
return process.env.AIRTAP_IS_SECURE_ENV === 'false'
}
if (process.env.CI) {
if (isForkPr()) return true
if (process.env.TRAVIS_SECURE_ENV_VARS === 'false') return true
}
return false
}
function isObject (o) {
return typeof o === 'object' && o !== null && !Array.isArray(o)
}
class NotFoundError extends Error {
constructor (input) {
super('No matching manifest found')
Object.defineProperty(this, 'name', { value: 'NotFoundError' })
Object.defineProperty(this, 'code', { value: 'ERR_MANIFEST_NOT_FOUND' })
Object.defineProperty(this, 'expected', { value: true })
Object.defineProperty(this, 'input', { value: input })
}
}
{
"name": "airtap-match-browsers",
"version": "0.1.0",
"version": "1.0.0",
"description": "Match browser manifests to a desired set of browsers",

@@ -8,3 +8,3 @@ "license": "MIT",

"scripts": {
"test": "standard && node test"
"test": "standard && nyc node test"
},

@@ -18,5 +18,7 @@ "files": [

"deep-equal": "^2.0.1",
"is-fork-pr": "^2.5.0",
"merge-deep": "^3.0.2"
},
"devDependencies": {
"nyc": "^15.1.0",
"standard": "^14.3.1",

@@ -23,0 +25,0 @@ "tape": "^4.13.0"

@@ -42,6 +42,6 @@ # airtap-match-browsers

- An exact or partial version ("6" matches "6.0").
- A keyword, one of "oldest" (first version) or "latest" (last numeric version).
- A keyword, one of "oldest" (first version) or "latest" (last stable version).
- A range in the form of `<start>..<end>`, where `start` and `end` are either a version or a keyword. This will result in one or more matches.
- A negative range in the form of `-<n>..latest`, for example `-1..latest` which means the last 2 numeric versions.
- 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".
- A negative range in the form of `-<n>..latest`, for example `-1..latest` which means the last 2 stable versions.
- A prerelease version like "dev", "beta", "80.0a1". Such versions sort after stable versions, so that `oldest..latest` excludes "dev" and `latest..dev` includes e.g. latest, "beta" and "dev".
- An array of versions.

@@ -48,0 +48,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc