ヤミRoot VoidGate
User / IP
:
216.73.216.81
Host / Server
:
146.88.233.70 / dev.loger.cm
System
:
Linux hybrid1120.fr.ns.planethoster.net 3.10.0-957.21.2.el7.x86_64 #1 SMP Wed Jun 5 14:26:44 UTC 2019 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
logercm
/
dev.loger.cm
/
fixtures
/
assert
/
Viewing: lib.tar
private/generate-name.js 0000644 00000001427 15120134631 0011264 0 ustar 00 "use strict"; var d = require("d"); var create = Object.create, defineProperty = Object.defineProperty, objPrototype = Object.prototype; var created = create(null); module.exports = function (desc) { var postfix = 0, name, ie11BugWorkaround; while (created[desc + (postfix || "")]) ++postfix; desc += postfix || ""; created[desc] = true; name = "@@" + desc; defineProperty( objPrototype, name, d.gs(null, function (value) { // For IE11 issue see: // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/ // ie11-broken-getters-on-dom-objects // https://github.com/medikoo/es6-symbol/issues/12 if (ie11BugWorkaround) return; ie11BugWorkaround = true; defineProperty(this, name, d(value)); ie11BugWorkaround = false; }) ); return name; }; private/setup/standard-symbols.js 0000644 00000002615 15120134631 0013202 0 ustar 00 "use strict"; var d = require("d") , NativeSymbol = require("ext/global-this").Symbol; module.exports = function (SymbolPolyfill) { return Object.defineProperties(SymbolPolyfill, { // To ensure proper interoperability with other native functions (e.g. Array.from) // fallback to eventual native implementation of given symbol hasInstance: d( "", (NativeSymbol && NativeSymbol.hasInstance) || SymbolPolyfill("hasInstance") ), isConcatSpreadable: d( "", (NativeSymbol && NativeSymbol.isConcatSpreadable) || SymbolPolyfill("isConcatSpreadable") ), iterator: d("", (NativeSymbol && NativeSymbol.iterator) || SymbolPolyfill("iterator")), match: d("", (NativeSymbol && NativeSymbol.match) || SymbolPolyfill("match")), replace: d("", (NativeSymbol && NativeSymbol.replace) || SymbolPolyfill("replace")), search: d("", (NativeSymbol && NativeSymbol.search) || SymbolPolyfill("search")), species: d("", (NativeSymbol && NativeSymbol.species) || SymbolPolyfill("species")), split: d("", (NativeSymbol && NativeSymbol.split) || SymbolPolyfill("split")), toPrimitive: d( "", (NativeSymbol && NativeSymbol.toPrimitive) || SymbolPolyfill("toPrimitive") ), toStringTag: d( "", (NativeSymbol && NativeSymbol.toStringTag) || SymbolPolyfill("toStringTag") ), unscopables: d( "", (NativeSymbol && NativeSymbol.unscopables) || SymbolPolyfill("unscopables") ) }); }; private/setup/symbol-registry.js 0000644 00000001054 15120134631 0013063 0 ustar 00 "use strict"; var d = require("d") , validateSymbol = require("../../../validate-symbol"); var registry = Object.create(null); module.exports = function (SymbolPolyfill) { return Object.defineProperties(SymbolPolyfill, { for: d(function (key) { if (registry[key]) return registry[key]; return (registry[key] = SymbolPolyfill(String(key))); }), keyFor: d(function (symbol) { var key; validateSymbol(symbol); for (key in registry) { if (registry[key] === symbol) return key; } return undefined; }) }); }; extract_description.js 0000644 00000001043 15120137125 0011152 0 ustar 00 module.exports = extractDescription // Extracts description from contents of a readme file in markdown format function extractDescription (d) { if (!d) { return } if (d === 'ERROR: No README data found!') { return } // the first block of text before the first heading // that isn't the first line heading d = d.trim().split('\n') for (var s = 0; d[s] && d[s].trim().match(/^(#|$)/); s++) { ; } var l = d.length for (var e = s + 1; e < l && d[e].trim(); e++) { ; } return d.slice(s, e).join(' ').trim() } fixer.js 0000644 00000030342 15120137125 0006216 0 ustar 00 var isValidSemver = require('semver/functions/valid') var cleanSemver = require('semver/functions/clean') var validateLicense = require('validate-npm-package-license') var hostedGitInfo = require('hosted-git-info') var isBuiltinModule = require('is-core-module') var depTypes = ['dependencies', 'devDependencies', 'optionalDependencies'] var extractDescription = require('./extract_description') var url = require('url') var typos = require('./typos.json') module.exports = { // default warning function warn: function () {}, fixRepositoryField: function (data) { if (data.repositories) { this.warn('repositories') data.repository = data.repositories[0] } if (!data.repository) { return this.warn('missingRepository') } if (typeof data.repository === 'string') { data.repository = { type: 'git', url: data.repository, } } var r = data.repository.url || '' if (r) { var hosted = hostedGitInfo.fromUrl(r) if (hosted) { r = data.repository.url = hosted.getDefaultRepresentation() === 'shortcut' ? hosted.https() : hosted.toString() } } if (r.match(/github.com\/[^/]+\/[^/]+\.git\.git$/)) { this.warn('brokenGitUrl', r) } }, fixTypos: function (data) { Object.keys(typos.topLevel).forEach(function (d) { if (Object.prototype.hasOwnProperty.call(data, d)) { this.warn('typo', d, typos.topLevel[d]) } }, this) }, fixScriptsField: function (data) { if (!data.scripts) { return } if (typeof data.scripts !== 'object') { this.warn('nonObjectScripts') delete data.scripts return } Object.keys(data.scripts).forEach(function (k) { if (typeof data.scripts[k] !== 'string') { this.warn('nonStringScript') delete data.scripts[k] } else if (typos.script[k] && !data.scripts[typos.script[k]]) { this.warn('typo', k, typos.script[k], 'scripts') } }, this) }, fixFilesField: function (data) { var files = data.files if (files && !Array.isArray(files)) { this.warn('nonArrayFiles') delete data.files } else if (data.files) { data.files = data.files.filter(function (file) { if (!file || typeof file !== 'string') { this.warn('invalidFilename', file) return false } else { return true } }, this) } }, fixBinField: function (data) { if (!data.bin) { return } if (typeof data.bin === 'string') { var b = {} var match if (match = data.name.match(/^@[^/]+[/](.*)$/)) { b[match[1]] = data.bin } else { b[data.name] = data.bin } data.bin = b } }, fixManField: function (data) { if (!data.man) { return } if (typeof data.man === 'string') { data.man = [data.man] } }, fixBundleDependenciesField: function (data) { var bdd = 'bundledDependencies' var bd = 'bundleDependencies' if (data[bdd] && !data[bd]) { data[bd] = data[bdd] delete data[bdd] } if (data[bd] && !Array.isArray(data[bd])) { this.warn('nonArrayBundleDependencies') delete data[bd] } else if (data[bd]) { data[bd] = data[bd].filter(function (bd) { if (!bd || typeof bd !== 'string') { this.warn('nonStringBundleDependency', bd) return false } else { if (!data.dependencies) { data.dependencies = {} } if (Object.prototype.hasOwnProperty.call(data.dependencies, bd)) { this.warn('nonDependencyBundleDependency', bd) data.dependencies[bd] = '*' } return true } }, this) } }, fixDependencies: function (data, strict) { objectifyDeps(data, this.warn) addOptionalDepsToDeps(data, this.warn) this.fixBundleDependenciesField(data) ;['dependencies', 'devDependencies'].forEach(function (deps) { if (!(deps in data)) { return } if (!data[deps] || typeof data[deps] !== 'object') { this.warn('nonObjectDependencies', deps) delete data[deps] return } Object.keys(data[deps]).forEach(function (d) { var r = data[deps][d] if (typeof r !== 'string') { this.warn('nonStringDependency', d, JSON.stringify(r)) delete data[deps][d] } var hosted = hostedGitInfo.fromUrl(data[deps][d]) if (hosted) { data[deps][d] = hosted.toString() } }, this) }, this) }, fixModulesField: function (data) { if (data.modules) { this.warn('deprecatedModules') delete data.modules } }, fixKeywordsField: function (data) { if (typeof data.keywords === 'string') { data.keywords = data.keywords.split(/,\s+/) } if (data.keywords && !Array.isArray(data.keywords)) { delete data.keywords this.warn('nonArrayKeywords') } else if (data.keywords) { data.keywords = data.keywords.filter(function (kw) { if (typeof kw !== 'string' || !kw) { this.warn('nonStringKeyword') return false } else { return true } }, this) } }, fixVersionField: function (data, strict) { // allow "loose" semver 1.0 versions in non-strict mode // enforce strict semver 2.0 compliance in strict mode var loose = !strict if (!data.version) { data.version = '' return true } if (!isValidSemver(data.version, loose)) { throw new Error('Invalid version: "' + data.version + '"') } data.version = cleanSemver(data.version, loose) return true }, fixPeople: function (data) { modifyPeople(data, unParsePerson) modifyPeople(data, parsePerson) }, fixNameField: function (data, options) { if (typeof options === 'boolean') { options = {strict: options} } else if (typeof options === 'undefined') { options = {} } var strict = options.strict if (!data.name && !strict) { data.name = '' return } if (typeof data.name !== 'string') { throw new Error('name field must be a string.') } if (!strict) { data.name = data.name.trim() } ensureValidName(data.name, strict, options.allowLegacyCase) if (isBuiltinModule(data.name)) { this.warn('conflictingName', data.name) } }, fixDescriptionField: function (data) { if (data.description && typeof data.description !== 'string') { this.warn('nonStringDescription') delete data.description } if (data.readme && !data.description) { data.description = extractDescription(data.readme) } if (data.description === undefined) { delete data.description } if (!data.description) { this.warn('missingDescription') } }, fixReadmeField: function (data) { if (!data.readme) { this.warn('missingReadme') data.readme = 'ERROR: No README data found!' } }, fixBugsField: function (data) { if (!data.bugs && data.repository && data.repository.url) { var hosted = hostedGitInfo.fromUrl(data.repository.url) if (hosted && hosted.bugs()) { data.bugs = {url: hosted.bugs()} } } else if (data.bugs) { var emailRe = /^.+@.*\..+$/ if (typeof data.bugs === 'string') { if (emailRe.test(data.bugs)) { data.bugs = {email: data.bugs} /* eslint-disable-next-line node/no-deprecated-api */ } else if (url.parse(data.bugs).protocol) { data.bugs = {url: data.bugs} } else { this.warn('nonEmailUrlBugsString') } } else { bugsTypos(data.bugs, this.warn) var oldBugs = data.bugs data.bugs = {} if (oldBugs.url) { /* eslint-disable-next-line node/no-deprecated-api */ if (typeof (oldBugs.url) === 'string' && url.parse(oldBugs.url).protocol) { data.bugs.url = oldBugs.url } else { this.warn('nonUrlBugsUrlField') } } if (oldBugs.email) { if (typeof (oldBugs.email) === 'string' && emailRe.test(oldBugs.email)) { data.bugs.email = oldBugs.email } else { this.warn('nonEmailBugsEmailField') } } } if (!data.bugs.email && !data.bugs.url) { delete data.bugs this.warn('emptyNormalizedBugs') } } }, fixHomepageField: function (data) { if (!data.homepage && data.repository && data.repository.url) { var hosted = hostedGitInfo.fromUrl(data.repository.url) if (hosted && hosted.docs()) { data.homepage = hosted.docs() } } if (!data.homepage) { return } if (typeof data.homepage !== 'string') { this.warn('nonUrlHomepage') return delete data.homepage } /* eslint-disable-next-line node/no-deprecated-api */ if (!url.parse(data.homepage).protocol) { data.homepage = 'http://' + data.homepage } }, fixLicenseField: function (data) { const license = data.license || data.licence if (!license) { return this.warn('missingLicense') } if ( typeof (license) !== 'string' || license.length < 1 || license.trim() === '' ) { return this.warn('invalidLicense') } if (!validateLicense(license).validForNewPackages) { return this.warn('invalidLicense') } }, } function isValidScopedPackageName (spec) { if (spec.charAt(0) !== '@') { return false } var rest = spec.slice(1).split('/') if (rest.length !== 2) { return false } return rest[0] && rest[1] && rest[0] === encodeURIComponent(rest[0]) && rest[1] === encodeURIComponent(rest[1]) } function isCorrectlyEncodedName (spec) { return !spec.match(/[/@\s+%:]/) && spec === encodeURIComponent(spec) } function ensureValidName (name, strict, allowLegacyCase) { if (name.charAt(0) === '.' || !(isValidScopedPackageName(name) || isCorrectlyEncodedName(name)) || (strict && (!allowLegacyCase) && name !== name.toLowerCase()) || name.toLowerCase() === 'node_modules' || name.toLowerCase() === 'favicon.ico') { throw new Error('Invalid name: ' + JSON.stringify(name)) } } function modifyPeople (data, fn) { if (data.author) { data.author = fn(data.author) }['maintainers', 'contributors'].forEach(function (set) { if (!Array.isArray(data[set])) { return } data[set] = data[set].map(fn) }) return data } function unParsePerson (person) { if (typeof person === 'string') { return person } var name = person.name || '' var u = person.url || person.web var url = u ? (' (' + u + ')') : '' var e = person.email || person.mail var email = e ? (' <' + e + '>') : '' return name + email + url } function parsePerson (person) { if (typeof person !== 'string') { return person } var name = person.match(/^([^(<]+)/) var url = person.match(/\(([^)]+)\)/) var email = person.match(/<([^>]+)>/) var obj = {} if (name && name[0].trim()) { obj.name = name[0].trim() } if (email) { obj.email = email[1] } if (url) { obj.url = url[1] } return obj } function addOptionalDepsToDeps (data, warn) { var o = data.optionalDependencies if (!o) { return } var d = data.dependencies || {} Object.keys(o).forEach(function (k) { d[k] = o[k] }) data.dependencies = d } function depObjectify (deps, type, warn) { if (!deps) { return {} } if (typeof deps === 'string') { deps = deps.trim().split(/[\n\r\s\t ,]+/) } if (!Array.isArray(deps)) { return deps } warn('deprecatedArrayDependencies', type) var o = {} deps.filter(function (d) { return typeof d === 'string' }).forEach(function (d) { d = d.trim().split(/(:?[@\s><=])/) var dn = d.shift() var dv = d.join('') dv = dv.trim() dv = dv.replace(/^@/, '') o[dn] = dv }) return o } function objectifyDeps (data, warn) { depTypes.forEach(function (type) { if (!data[type]) { return } data[type] = depObjectify(data[type], type, warn) }) } function bugsTypos (bugs, warn) { if (!bugs) { return } Object.keys(bugs).forEach(function (k) { if (typos.bugs[k]) { warn('typo', k, typos.bugs[k], 'bugs') bugs[typos.bugs[k]] = bugs[k] delete bugs[k] } }) } make_warning.js 0000644 00000001307 15120137125 0007542 0 ustar 00 var util = require('util') var messages = require('./warning_messages.json') module.exports = function () { var args = Array.prototype.slice.call(arguments, 0) var warningName = args.shift() if (warningName === 'typo') { return makeTypoWarning.apply(null, args) } else { var msgTemplate = messages[warningName] ? messages[warningName] : warningName + ": '%s'" args.unshift(msgTemplate) return util.format.apply(null, args) } } function makeTypoWarning (providedName, probableName, field) { if (field) { providedName = field + "['" + providedName + "']" probableName = field + "['" + probableName + "']" } return util.format(messages.typo, providedName, probableName) } normalize.js 0000644 00000002547 15120137125 0007107 0 ustar 00 module.exports = normalize var fixer = require('./fixer') normalize.fixer = fixer var makeWarning = require('./make_warning') var fieldsToFix = ['name', 'version', 'description', 'repository', 'modules', 'scripts', 'files', 'bin', 'man', 'bugs', 'keywords', 'readme', 'homepage', 'license'] var otherThingsToFix = ['dependencies', 'people', 'typos'] var thingsToFix = fieldsToFix.map(function (fieldName) { return ucFirst(fieldName) + 'Field' }) // two ways to do this in CoffeeScript on only one line, sub-70 chars: // thingsToFix = fieldsToFix.map (name) -> ucFirst(name) + "Field" // thingsToFix = (ucFirst(name) + "Field" for name in fieldsToFix) thingsToFix = thingsToFix.concat(otherThingsToFix) function normalize (data, warn, strict) { if (warn === true) { warn = null strict = true } if (!strict) { strict = false } if (!warn || data.private) { warn = function (msg) { /* noop */ } } if (data.scripts && data.scripts.install === 'node-gyp rebuild' && !data.scripts.preinstall) { data.gypfile = true } fixer.warn = function () { warn(makeWarning.apply(null, arguments)) } thingsToFix.forEach(function (thingName) { fixer['fix' + ucFirst(thingName)](data, strict) }) data._id = data.name + '@' + data.version } function ucFirst (string) { return string.charAt(0).toUpperCase() + string.slice(1) } safe_format.js 0000644 00000000406 15120137125 0007365 0 ustar 00 var util = require('util') module.exports = function () { var args = Array.prototype.slice.call(arguments, 0) args.forEach(function (arg) { if (!arg) { throw new TypeError('Bad arguments.') } }) return util.format.apply(null, arguments) } typos.json 0000644 00000001353 15120137125 0006614 0 ustar 00 { "topLevel": { "dependancies": "dependencies" ,"dependecies": "dependencies" ,"depdenencies": "dependencies" ,"devEependencies": "devDependencies" ,"depends": "dependencies" ,"dev-dependencies": "devDependencies" ,"devDependences": "devDependencies" ,"devDepenencies": "devDependencies" ,"devdependencies": "devDependencies" ,"repostitory": "repository" ,"repo": "repository" ,"prefereGlobal": "preferGlobal" ,"hompage": "homepage" ,"hampage": "homepage" ,"autohr": "author" ,"autor": "author" ,"contributers": "contributors" ,"publicationConfig": "publishConfig" ,"script": "scripts" }, "bugs": { "web": "url", "name": "url" }, "script": { "server": "start", "tests": "test" } } warning_messages.json 0000644 00000003406 15120137125 0010773 0 ustar 00 { "repositories": "'repositories' (plural) Not supported. Please pick one as the 'repository' field" ,"missingRepository": "No repository field." ,"brokenGitUrl": "Probably broken git url: %s" ,"nonObjectScripts": "scripts must be an object" ,"nonStringScript": "script values must be string commands" ,"nonArrayFiles": "Invalid 'files' member" ,"invalidFilename": "Invalid filename in 'files' list: %s" ,"nonArrayBundleDependencies": "Invalid 'bundleDependencies' list. Must be array of package names" ,"nonStringBundleDependency": "Invalid bundleDependencies member: %s" ,"nonDependencyBundleDependency": "Non-dependency in bundleDependencies: %s" ,"nonObjectDependencies": "%s field must be an object" ,"nonStringDependency": "Invalid dependency: %s %s" ,"deprecatedArrayDependencies": "specifying %s as array is deprecated" ,"deprecatedModules": "modules field is deprecated" ,"nonArrayKeywords": "keywords should be an array of strings" ,"nonStringKeyword": "keywords should be an array of strings" ,"conflictingName": "%s is also the name of a node core module." ,"nonStringDescription": "'description' field should be a string" ,"missingDescription": "No description" ,"missingReadme": "No README data" ,"missingLicense": "No license field." ,"nonEmailUrlBugsString": "Bug string field must be url, email, or {email,url}" ,"nonUrlBugsUrlField": "bugs.url field must be a string url. Deleted." ,"nonEmailBugsEmailField": "bugs.email field must be a string email. Deleted." ,"emptyNormalizedBugs": "Normalized value of bugs field is an empty object. Deleted." ,"nonUrlHomepage": "homepage field must be a string url. Deleted." ,"invalidLicense": "license should be a valid SPDX license expression" ,"typo": "%s should probably be %s." } encoding.js 0000644 00000004104 15120137617 0006672 0 ustar 00 'use strict'; var iconvLite = require('iconv-lite'); // Expose to the world module.exports.convert = convert; /** * Convert encoding of an UTF-8 string or a buffer * * @param {String|Buffer} str String to be converted * @param {String} to Encoding to be converted to * @param {String} [from='UTF-8'] Encoding to be converted from * @return {Buffer} Encoded string */ function convert(str, to, from) { from = checkEncoding(from || 'UTF-8'); to = checkEncoding(to || 'UTF-8'); str = str || ''; var result; if (from !== 'UTF-8' && typeof str === 'string') { str = Buffer.from(str, 'binary'); } if (from === to) { if (typeof str === 'string') { result = Buffer.from(str); } else { result = str; } } else { try { result = convertIconvLite(str, to, from); } catch (E) { console.error(E); result = str; } } if (typeof result === 'string') { result = Buffer.from(result, 'utf-8'); } return result; } /** * Convert encoding of astring with iconv-lite * * @param {String|Buffer} str String to be converted * @param {String} to Encoding to be converted to * @param {String} [from='UTF-8'] Encoding to be converted from * @return {Buffer} Encoded string */ function convertIconvLite(str, to, from) { if (to === 'UTF-8') { return iconvLite.decode(str, from); } else if (from === 'UTF-8') { return iconvLite.encode(str, to); } else { return iconvLite.encode(iconvLite.decode(str, from), to); } } /** * Converts charset name if needed * * @param {String} name Character set * @return {String} Character set name */ function checkEncoding(name) { return (name || '') .toString() .trim() .replace(/^latin[\-_]?(\d+)$/i, 'ISO-8859-$1') .replace(/^win(?:dows)?[\-_]?(\d+)$/i, 'WINDOWS-$1') .replace(/^utf[\-_]?(\d+)$/i, 'UTF-$1') .replace(/^ks_c_5601\-1987$/i, 'CP949') .replace(/^us[\-_]?ascii$/i, 'ASCII') .toUpperCase(); } ipaddr.js 0000644 00000102737 15120140412 0006345 0 ustar 00 (function (root) { 'use strict'; // A list of regular expressions that match arbitrary IPv4 addresses, // for which a number of weird notations exist. // Note that an address like 0010.0xa5.1.1 is considered legal. const ipv4Part = '(0?\\d+|0x[a-f0-9]+)'; const ipv4Regexes = { fourOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'), threeOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'), twoOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}$`, 'i'), longValue: new RegExp(`^${ipv4Part}$`, 'i') }; // Regular Expression for checking Octal numbers const octalRegex = new RegExp(`^0[0-7]+$`, 'i'); const hexRegex = new RegExp(`^0x[a-f0-9]+$`, 'i'); const zoneIndex = '%[0-9a-z]{1,}'; // IPv6-matching regular expressions. // For IPv6, the task is simpler: it is enough to match the colon-delimited // hexadecimal IPv6 and a transitional variant with dotted-decimal IPv4 at // the end. const ipv6Part = '(?:[0-9a-f]+::?)+'; const ipv6Regexes = { zoneIndex: new RegExp(zoneIndex, 'i'), 'native': new RegExp(`^(::)?(${ipv6Part})?([0-9a-f]+)?(::)?(${zoneIndex})?$`, 'i'), deprecatedTransitional: new RegExp(`^(?:::)(${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?)$`, 'i'), transitional: new RegExp(`^((?:${ipv6Part})|(?:::)(?:${ipv6Part})?)${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?$`, 'i') }; // Expand :: in an IPv6 address or address part consisting of `parts` groups. function expandIPv6 (string, parts) { // More than one '::' means invalid adddress if (string.indexOf('::') !== string.lastIndexOf('::')) { return null; } let colonCount = 0; let lastColon = -1; let zoneId = (string.match(ipv6Regexes.zoneIndex) || [])[0]; let replacement, replacementCount; // Remove zone index and save it for later if (zoneId) { zoneId = zoneId.substring(1); string = string.replace(/%.+$/, ''); } // How many parts do we already have? while ((lastColon = string.indexOf(':', lastColon + 1)) >= 0) { colonCount++; } // 0::0 is two parts more than :: if (string.substr(0, 2) === '::') { colonCount--; } if (string.substr(-2, 2) === '::') { colonCount--; } // The following loop would hang if colonCount > parts if (colonCount > parts) { return null; } // replacement = ':' + '0:' * (parts - colonCount) replacementCount = parts - colonCount; replacement = ':'; while (replacementCount--) { replacement += '0:'; } // Insert the missing zeroes string = string.replace('::', replacement); // Trim any garbage which may be hanging around if :: was at the edge in // the source strin if (string[0] === ':') { string = string.slice(1); } if (string[string.length - 1] === ':') { string = string.slice(0, -1); } parts = (function () { const ref = string.split(':'); const results = []; for (let i = 0; i < ref.length; i++) { results.push(parseInt(ref[i], 16)); } return results; })(); return { parts: parts, zoneId: zoneId }; } // A generic CIDR (Classless Inter-Domain Routing) RFC1518 range matcher. function matchCIDR (first, second, partSize, cidrBits) { if (first.length !== second.length) { throw new Error('ipaddr: cannot match CIDR for objects with different lengths'); } let part = 0; let shift; while (cidrBits > 0) { shift = partSize - cidrBits; if (shift < 0) { shift = 0; } if (first[part] >> shift !== second[part] >> shift) { return false; } cidrBits -= partSize; part += 1; } return true; } function parseIntAuto (string) { // Hexadedimal base 16 (0x#) if (hexRegex.test(string)) { return parseInt(string, 16); } // While octal representation is discouraged by ECMAScript 3 // and forbidden by ECMAScript 5, we silently allow it to // work only if the rest of the string has numbers less than 8. if (string[0] === '0' && !isNaN(parseInt(string[1], 10))) { if (octalRegex.test(string)) { return parseInt(string, 8); } throw new Error(`ipaddr: cannot parse ${string} as octal`); } // Always include the base 10 radix! return parseInt(string, 10); } function padPart (part, length) { while (part.length < length) { part = `0${part}`; } return part; } const ipaddr = {}; // An IPv4 address (RFC791). ipaddr.IPv4 = (function () { // Constructs a new IPv4 address from an array of four octets // in network order (MSB first) // Verifies the input. function IPv4 (octets) { if (octets.length !== 4) { throw new Error('ipaddr: ipv4 octet count should be 4'); } let i, octet; for (i = 0; i < octets.length; i++) { octet = octets[i]; if (!((0 <= octet && octet <= 255))) { throw new Error('ipaddr: ipv4 octet should fit in 8 bits'); } } this.octets = octets; } // Special IPv4 address ranges. // See also https://en.wikipedia.org/wiki/Reserved_IP_addresses IPv4.prototype.SpecialRanges = { unspecified: [[new IPv4([0, 0, 0, 0]), 8]], broadcast: [[new IPv4([255, 255, 255, 255]), 32]], // RFC3171 multicast: [[new IPv4([224, 0, 0, 0]), 4]], // RFC3927 linkLocal: [[new IPv4([169, 254, 0, 0]), 16]], // RFC5735 loopback: [[new IPv4([127, 0, 0, 0]), 8]], // RFC6598 carrierGradeNat: [[new IPv4([100, 64, 0, 0]), 10]], // RFC1918 'private': [ [new IPv4([10, 0, 0, 0]), 8], [new IPv4([172, 16, 0, 0]), 12], [new IPv4([192, 168, 0, 0]), 16] ], // Reserved and testing-only ranges; RFCs 5735, 5737, 2544, 1700 reserved: [ [new IPv4([192, 0, 0, 0]), 24], [new IPv4([192, 0, 2, 0]), 24], [new IPv4([192, 88, 99, 0]), 24], [new IPv4([198, 18, 0, 0]), 15], [new IPv4([198, 51, 100, 0]), 24], [new IPv4([203, 0, 113, 0]), 24], [new IPv4([240, 0, 0, 0]), 4] ] }; // The 'kind' method exists on both IPv4 and IPv6 classes. IPv4.prototype.kind = function () { return 'ipv4'; }; // Checks if this address matches other one within given CIDR range. IPv4.prototype.match = function (other, cidrRange) { let ref; if (cidrRange === undefined) { ref = other; other = ref[0]; cidrRange = ref[1]; } if (other.kind() !== 'ipv4') { throw new Error('ipaddr: cannot match ipv4 address with non-ipv4 one'); } return matchCIDR(this.octets, other.octets, 8, cidrRange); }; // returns a number of leading ones in IPv4 address, making sure that // the rest is a solid sequence of 0's (valid netmask) // returns either the CIDR length or null if mask is not valid IPv4.prototype.prefixLengthFromSubnetMask = function () { let cidr = 0; // non-zero encountered stop scanning for zeroes let stop = false; // number of zeroes in octet const zerotable = { 0: 8, 128: 7, 192: 6, 224: 5, 240: 4, 248: 3, 252: 2, 254: 1, 255: 0 }; let i, octet, zeros; for (i = 3; i >= 0; i -= 1) { octet = this.octets[i]; if (octet in zerotable) { zeros = zerotable[octet]; if (stop && zeros !== 0) { return null; } if (zeros !== 8) { stop = true; } cidr += zeros; } else { return null; } } return 32 - cidr; }; // Checks if the address corresponds to one of the special ranges. IPv4.prototype.range = function () { return ipaddr.subnetMatch(this, this.SpecialRanges); }; // Returns an array of byte-sized values in network order (MSB first) IPv4.prototype.toByteArray = function () { return this.octets.slice(0); }; // Converts this IPv4 address to an IPv4-mapped IPv6 address. IPv4.prototype.toIPv4MappedAddress = function () { return ipaddr.IPv6.parse(`::ffff:${this.toString()}`); }; // Symmetrical method strictly for aligning with the IPv6 methods. IPv4.prototype.toNormalizedString = function () { return this.toString(); }; // Returns the address in convenient, decimal-dotted format. IPv4.prototype.toString = function () { return this.octets.join('.'); }; return IPv4; })(); // A utility function to return broadcast address given the IPv4 interface and prefix length in CIDR notation ipaddr.IPv4.broadcastAddressFromCIDR = function (string) { try { const cidr = this.parseCIDR(string); const ipInterfaceOctets = cidr[0].toByteArray(); const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); const octets = []; let i = 0; while (i < 4) { // Broadcast address is bitwise OR between ip interface and inverted mask octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255); i++; } return new this(octets); } catch (e) { throw new Error('ipaddr: the address does not have IPv4 CIDR format'); } }; // Checks if a given string is formatted like IPv4 address. ipaddr.IPv4.isIPv4 = function (string) { return this.parser(string) !== null; }; // Checks if a given string is a valid IPv4 address. ipaddr.IPv4.isValid = function (string) { try { new this(this.parser(string)); return true; } catch (e) { return false; } }; // Checks if a given string is a full four-part IPv4 Address. ipaddr.IPv4.isValidFourPartDecimal = function (string) { if (ipaddr.IPv4.isValid(string) && string.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/)) { return true; } else { return false; } }; // A utility function to return network address given the IPv4 interface and prefix length in CIDR notation ipaddr.IPv4.networkAddressFromCIDR = function (string) { let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; try { cidr = this.parseCIDR(string); ipInterfaceOctets = cidr[0].toByteArray(); subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); octets = []; i = 0; while (i < 4) { // Network address is bitwise AND between ip interface and mask octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10)); i++; } return new this(octets); } catch (e) { throw new Error('ipaddr: the address does not have IPv4 CIDR format'); } }; // Tries to parse and validate a string with IPv4 address. // Throws an error if it fails. ipaddr.IPv4.parse = function (string) { const parts = this.parser(string); if (parts === null) { throw new Error('ipaddr: string is not formatted like an IPv4 Address'); } return new this(parts); }; // Parses the string as an IPv4 Address with CIDR Notation. ipaddr.IPv4.parseCIDR = function (string) { let match; if ((match = string.match(/^(.+)\/(\d+)$/))) { const maskLength = parseInt(match[2]); if (maskLength >= 0 && maskLength <= 32) { const parsed = [this.parse(match[1]), maskLength]; Object.defineProperty(parsed, 'toString', { value: function () { return this.join('/'); } }); return parsed; } } throw new Error('ipaddr: string is not formatted like an IPv4 CIDR range'); }; // Classful variants (like a.b, where a is an octet, and b is a 24-bit // value representing last three octets; this corresponds to a class C // address) are omitted due to classless nature of modern Internet. ipaddr.IPv4.parser = function (string) { let match, part, value; // parseInt recognizes all that octal & hexadecimal weirdness for us if ((match = string.match(ipv4Regexes.fourOctet))) { return (function () { const ref = match.slice(1, 6); const results = []; for (let i = 0; i < ref.length; i++) { part = ref[i]; results.push(parseIntAuto(part)); } return results; })(); } else if ((match = string.match(ipv4Regexes.longValue))) { value = parseIntAuto(match[1]); if (value > 0xffffffff || value < 0) { throw new Error('ipaddr: address outside defined range'); } return ((function () { const results = []; let shift; for (shift = 0; shift <= 24; shift += 8) { results.push((value >> shift) & 0xff); } return results; })()).reverse(); } else if ((match = string.match(ipv4Regexes.twoOctet))) { return (function () { const ref = match.slice(1, 4); const results = []; value = parseIntAuto(ref[1]); if (value > 0xffffff || value < 0) { throw new Error('ipaddr: address outside defined range'); } results.push(parseIntAuto(ref[0])); results.push((value >> 16) & 0xff); results.push((value >> 8) & 0xff); results.push( value & 0xff); return results; })(); } else if ((match = string.match(ipv4Regexes.threeOctet))) { return (function () { const ref = match.slice(1, 5); const results = []; value = parseIntAuto(ref[2]); if (value > 0xffff || value < 0) { throw new Error('ipaddr: address outside defined range'); } results.push(parseIntAuto(ref[0])); results.push(parseIntAuto(ref[1])); results.push((value >> 8) & 0xff); results.push( value & 0xff); return results; })(); } else { return null; } }; // A utility function to return subnet mask in IPv4 format given the prefix length ipaddr.IPv4.subnetMaskFromPrefixLength = function (prefix) { prefix = parseInt(prefix); if (prefix < 0 || prefix > 32) { throw new Error('ipaddr: invalid IPv4 prefix length'); } const octets = [0, 0, 0, 0]; let j = 0; const filledOctetCount = Math.floor(prefix / 8); while (j < filledOctetCount) { octets[j] = 255; j++; } if (filledOctetCount < 4) { octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8); } return new this(octets); }; // An IPv6 address (RFC2460) ipaddr.IPv6 = (function () { // Constructs an IPv6 address from an array of eight 16 - bit parts // or sixteen 8 - bit parts in network order(MSB first). // Throws an error if the input is invalid. function IPv6 (parts, zoneId) { let i, part; if (parts.length === 16) { this.parts = []; for (i = 0; i <= 14; i += 2) { this.parts.push((parts[i] << 8) | parts[i + 1]); } } else if (parts.length === 8) { this.parts = parts; } else { throw new Error('ipaddr: ipv6 part count should be 8 or 16'); } for (i = 0; i < this.parts.length; i++) { part = this.parts[i]; if (!((0 <= part && part <= 0xffff))) { throw new Error('ipaddr: ipv6 part should fit in 16 bits'); } } if (zoneId) { this.zoneId = zoneId; } } // Special IPv6 ranges IPv6.prototype.SpecialRanges = { // RFC4291, here and after unspecified: [new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128], linkLocal: [new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10], multicast: [new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8], loopback: [new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128], uniqueLocal: [new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7], ipv4Mapped: [new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96], // RFC6145 rfc6145: [new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96], // RFC6052 rfc6052: [new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96], // RFC3056 '6to4': [new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16], // RFC6052, RFC6146 teredo: [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32], // RFC4291 reserved: [[new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32]], benchmarking: [new IPv6([0x2001, 0x2, 0, 0, 0, 0, 0, 0]), 48], amt: [new IPv6([0x2001, 0x3, 0, 0, 0, 0, 0, 0]), 32], as112v6: [new IPv6([0x2001, 0x4, 0x112, 0, 0, 0, 0, 0]), 48], deprecated: [new IPv6([0x2001, 0x10, 0, 0, 0, 0, 0, 0]), 28], orchid2: [new IPv6([0x2001, 0x20, 0, 0, 0, 0, 0, 0]), 28] }; // Checks if this address is an IPv4-mapped IPv6 address. IPv6.prototype.isIPv4MappedAddress = function () { return this.range() === 'ipv4Mapped'; }; // The 'kind' method exists on both IPv4 and IPv6 classes. IPv6.prototype.kind = function () { return 'ipv6'; }; // Checks if this address matches other one within given CIDR range. IPv6.prototype.match = function (other, cidrRange) { let ref; if (cidrRange === undefined) { ref = other; other = ref[0]; cidrRange = ref[1]; } if (other.kind() !== 'ipv6') { throw new Error('ipaddr: cannot match ipv6 address with non-ipv6 one'); } return matchCIDR(this.parts, other.parts, 16, cidrRange); }; // returns a number of leading ones in IPv6 address, making sure that // the rest is a solid sequence of 0's (valid netmask) // returns either the CIDR length or null if mask is not valid IPv6.prototype.prefixLengthFromSubnetMask = function () { let cidr = 0; // non-zero encountered stop scanning for zeroes let stop = false; // number of zeroes in octet const zerotable = { 0: 16, 32768: 15, 49152: 14, 57344: 13, 61440: 12, 63488: 11, 64512: 10, 65024: 9, 65280: 8, 65408: 7, 65472: 6, 65504: 5, 65520: 4, 65528: 3, 65532: 2, 65534: 1, 65535: 0 }; let part, zeros; for (let i = 7; i >= 0; i -= 1) { part = this.parts[i]; if (part in zerotable) { zeros = zerotable[part]; if (stop && zeros !== 0) { return null; } if (zeros !== 16) { stop = true; } cidr += zeros; } else { return null; } } return 128 - cidr; }; // Checks if the address corresponds to one of the special ranges. IPv6.prototype.range = function () { return ipaddr.subnetMatch(this, this.SpecialRanges); }; // Returns an array of byte-sized values in network order (MSB first) IPv6.prototype.toByteArray = function () { let part; const bytes = []; const ref = this.parts; for (let i = 0; i < ref.length; i++) { part = ref[i]; bytes.push(part >> 8); bytes.push(part & 0xff); } return bytes; }; // Returns the address in expanded format with all zeroes included, like // 2001:0db8:0008:0066:0000:0000:0000:0001 IPv6.prototype.toFixedLengthString = function () { const addr = ((function () { const results = []; for (let i = 0; i < this.parts.length; i++) { results.push(padPart(this.parts[i].toString(16), 4)); } return results; }).call(this)).join(':'); let suffix = ''; if (this.zoneId) { suffix = `%${this.zoneId}`; } return addr + suffix; }; // Converts this address to IPv4 address if it is an IPv4-mapped IPv6 address. // Throws an error otherwise. IPv6.prototype.toIPv4Address = function () { if (!this.isIPv4MappedAddress()) { throw new Error('ipaddr: trying to convert a generic ipv6 address to ipv4'); } const ref = this.parts.slice(-2); const high = ref[0]; const low = ref[1]; return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]); }; // Returns the address in expanded format with all zeroes included, like // 2001:db8:8:66:0:0:0:1 // // Deprecated: use toFixedLengthString() instead. IPv6.prototype.toNormalizedString = function () { const addr = ((function () { const results = []; for (let i = 0; i < this.parts.length; i++) { results.push(this.parts[i].toString(16)); } return results; }).call(this)).join(':'); let suffix = ''; if (this.zoneId) { suffix = `%${this.zoneId}`; } return addr + suffix; }; // Returns the address in compact, human-readable format like // 2001:db8:8:66::1 // in line with RFC 5952 (see https://tools.ietf.org/html/rfc5952#section-4) IPv6.prototype.toRFC5952String = function () { const regex = /((^|:)(0(:|$)){2,})/g; const string = this.toNormalizedString(); let bestMatchIndex = 0; let bestMatchLength = -1; let match; while ((match = regex.exec(string))) { if (match[0].length > bestMatchLength) { bestMatchIndex = match.index; bestMatchLength = match[0].length; } } if (bestMatchLength < 0) { return string; } return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}`; }; // Returns the address in compact, human-readable format like // 2001:db8:8:66::1 // Calls toRFC5952String under the hood. IPv6.prototype.toString = function () { return this.toRFC5952String(); }; return IPv6; })(); // A utility function to return broadcast address given the IPv6 interface and prefix length in CIDR notation ipaddr.IPv6.broadcastAddressFromCIDR = function (string) { try { const cidr = this.parseCIDR(string); const ipInterfaceOctets = cidr[0].toByteArray(); const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); const octets = []; let i = 0; while (i < 16) { // Broadcast address is bitwise OR between ip interface and inverted mask octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255); i++; } return new this(octets); } catch (e) { throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); } }; // Checks if a given string is formatted like IPv6 address. ipaddr.IPv6.isIPv6 = function (string) { return this.parser(string) !== null; }; // Checks to see if string is a valid IPv6 Address ipaddr.IPv6.isValid = function (string) { // Since IPv6.isValid is always called first, this shortcut // provides a substantial performance gain. if (typeof string === 'string' && string.indexOf(':') === -1) { return false; } try { const addr = this.parser(string); new this(addr.parts, addr.zoneId); return true; } catch (e) { return false; } }; // A utility function to return network address given the IPv6 interface and prefix length in CIDR notation ipaddr.IPv6.networkAddressFromCIDR = function (string) { let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets; try { cidr = this.parseCIDR(string); ipInterfaceOctets = cidr[0].toByteArray(); subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray(); octets = []; i = 0; while (i < 16) { // Network address is bitwise AND between ip interface and mask octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10)); i++; } return new this(octets); } catch (e) { throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`); } }; // Tries to parse and validate a string with IPv6 address. // Throws an error if it fails. ipaddr.IPv6.parse = function (string) { const addr = this.parser(string); if (addr.parts === null) { throw new Error('ipaddr: string is not formatted like an IPv6 Address'); } return new this(addr.parts, addr.zoneId); }; ipaddr.IPv6.parseCIDR = function (string) { let maskLength, match, parsed; if ((match = string.match(/^(.+)\/(\d+)$/))) { maskLength = parseInt(match[2]); if (maskLength >= 0 && maskLength <= 128) { parsed = [this.parse(match[1]), maskLength]; Object.defineProperty(parsed, 'toString', { value: function () { return this.join('/'); } }); return parsed; } } throw new Error('ipaddr: string is not formatted like an IPv6 CIDR range'); }; // Parse an IPv6 address. ipaddr.IPv6.parser = function (string) { let addr, i, match, octet, octets, zoneId; if ((match = string.match(ipv6Regexes.deprecatedTransitional))) { return this.parser(`::ffff:${match[1]}`); } if (ipv6Regexes.native.test(string)) { return expandIPv6(string, 8); } if ((match = string.match(ipv6Regexes.transitional))) { zoneId = match[6] || ''; addr = expandIPv6(match[1].slice(0, -1) + zoneId, 6); if (addr.parts) { octets = [ parseInt(match[2]), parseInt(match[3]), parseInt(match[4]), parseInt(match[5]) ]; for (i = 0; i < octets.length; i++) { octet = octets[i]; if (!((0 <= octet && octet <= 255))) { return null; } } addr.parts.push(octets[0] << 8 | octets[1]); addr.parts.push(octets[2] << 8 | octets[3]); return { parts: addr.parts, zoneId: addr.zoneId }; } } return null; }; // A utility function to return subnet mask in IPv6 format given the prefix length ipaddr.IPv6.subnetMaskFromPrefixLength = function (prefix) { prefix = parseInt(prefix); if (prefix < 0 || prefix > 128) { throw new Error('ipaddr: invalid IPv6 prefix length'); } const octets = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let j = 0; const filledOctetCount = Math.floor(prefix / 8); while (j < filledOctetCount) { octets[j] = 255; j++; } if (filledOctetCount < 16) { octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8); } return new this(octets); }; // Try to parse an array in network order (MSB first) for IPv4 and IPv6 ipaddr.fromByteArray = function (bytes) { const length = bytes.length; if (length === 4) { return new ipaddr.IPv4(bytes); } else if (length === 16) { return new ipaddr.IPv6(bytes); } else { throw new Error('ipaddr: the binary input is neither an IPv6 nor IPv4 address'); } }; // Checks if the address is valid IP address ipaddr.isValid = function (string) { return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string); }; // Attempts to parse an IP Address, first through IPv6 then IPv4. // Throws an error if it could not be parsed. ipaddr.parse = function (string) { if (ipaddr.IPv6.isValid(string)) { return ipaddr.IPv6.parse(string); } else if (ipaddr.IPv4.isValid(string)) { return ipaddr.IPv4.parse(string); } else { throw new Error('ipaddr: the address has neither IPv6 nor IPv4 format'); } }; // Attempt to parse CIDR notation, first through IPv6 then IPv4. // Throws an error if it could not be parsed. ipaddr.parseCIDR = function (string) { try { return ipaddr.IPv6.parseCIDR(string); } catch (e) { try { return ipaddr.IPv4.parseCIDR(string); } catch (e2) { throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format'); } } }; // Parse an address and return plain IPv4 address if it is an IPv4-mapped address ipaddr.process = function (string) { const addr = this.parse(string); if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) { return addr.toIPv4Address(); } else { return addr; } }; // An utility function to ease named range matching. See examples below. // rangeList can contain both IPv4 and IPv6 subnet entries and will not throw errors // on matching IPv4 addresses to IPv6 ranges or vice versa. ipaddr.subnetMatch = function (address, rangeList, defaultName) { let i, rangeName, rangeSubnets, subnet; if (defaultName === undefined || defaultName === null) { defaultName = 'unicast'; } for (rangeName in rangeList) { if (Object.prototype.hasOwnProperty.call(rangeList, rangeName)) { rangeSubnets = rangeList[rangeName]; // ECMA5 Array.isArray isn't available everywhere if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) { rangeSubnets = [rangeSubnets]; } for (i = 0; i < rangeSubnets.length; i++) { subnet = rangeSubnets[i]; if (address.kind() === subnet[0].kind() && address.match.apply(address, subnet)) { return rangeName; } } } } return defaultName; }; // Export for both the CommonJS and browser-like environment if (typeof module !== 'undefined' && module.exports) { module.exports = ipaddr; } else { root.ipaddr = ipaddr; } }(this)); ipaddr.js.d.ts 0000644 00000005546 15120140412 0007214 0 ustar 00 declare module "ipaddr.js" { type IPvXRangeDefaults = 'unicast' | 'unspecified' | 'multicast' | 'linkLocal' | 'loopback' | 'reserved'; type IPv4Range = IPvXRangeDefaults | 'broadcast' | 'carrierGradeNat' | 'private'; type IPv6Range = IPvXRangeDefaults | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo'; interface RangeList<T> { [name: string]: [T, number] | [T, number][]; } // Common methods/properties for IPv4 and IPv6 classes. class IP { prefixLengthFromSubnetMask(): number | null; toByteArray(): number[]; toNormalizedString(): string; toString(): string; } namespace Address { export function fromByteArray(bytes: number[]): IPv4 | IPv6; export function isValid(addr: string): boolean; export function parse(addr: string): IPv4 | IPv6; export function parseCIDR(mask: string): [IPv4 | IPv6, number]; export function process(addr: string): IPv4 | IPv6; export function subnetMatch(addr: IPv4 | IPv6, rangeList: RangeList<IPv4 | IPv6>, defaultName?: string): string; export class IPv4 extends IP { static broadcastAddressFromCIDR(addr: string): IPv4; static isIPv4(addr: string): boolean; static isValidFourPartDecimal(addr: string): boolean; static isValid(addr: string): boolean; static networkAddressFromCIDR(addr: string): IPv4; static parse(addr: string): IPv4; static parseCIDR(addr: string): [IPv4, number]; static subnetMaskFromPrefixLength(prefix: number): IPv4; constructor(octets: number[]); octets: number[] kind(): 'ipv4'; match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; range(): IPv4Range; subnetMatch(rangeList: RangeList<IPv4>, defaultName?: string): string; toIPv4MappedAddress(): IPv6; } export class IPv6 extends IP { static broadcastAddressFromCIDR(addr: string): IPv6; static isIPv6(addr: string): boolean; static isValid(addr: string): boolean; static networkAddressFromCIDR(addr: string): IPv6; static parse(addr: string): IPv6; static parseCIDR(addr: string): [IPv6, number]; static subnetMaskFromPrefixLength(prefix: number): IPv6; constructor(parts: number[]); parts: number[] zoneId?: string isIPv4MappedAddress(): boolean; kind(): 'ipv6'; match(what: IPv4 | IPv6 | [IPv4 | IPv6, number], bits?: number): boolean; range(): IPv6Range; subnetMatch(rangeList: RangeList<IPv6>, defaultName?: string): string; toIPv4Address(): IPv4; toRFC5952String(): string; } } export = Address; } attributes.d.ts 0000644 00000000653 15120140443 0007522 0 ustar 00 import { CompiledQuery, InternalOptions } from "./types"; import type { AttributeSelector, AttributeAction } from "css-what"; /** * Attribute selectors */ export declare const attributeRules: Record<AttributeAction, <Node, ElementNode extends Node>(next: CompiledQuery<ElementNode>, data: AttributeSelector, options: InternalOptions<Node, ElementNode>) => CompiledQuery<ElementNode>>; //# sourceMappingURL=attributes.d.ts.map attributes.d.ts.map 0000644 00000000705 15120140443 0010274 0 ustar 00 {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AA+EnE;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAC/B,eAAe,EACf,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EAC3B,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,EAChC,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,KAC1C,aAAa,CAAC,WAAW,CAAC,CAsLlC,CAAC"} attributes.js 0000644 00000016562 15120140443 0007274 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.attributeRules = void 0; var boolbase_1 = require("boolbase"); /** * All reserved characters in a regex, used for escaping. * * Taken from XRegExp, (c) 2007-2020 Steven Levithan under the MIT license * https://github.com/slevithan/xregexp/blob/95eeebeb8fac8754d54eafe2b4743661ac1cf028/src/xregexp.js#L794 */ var reChars = /[-[\]{}()*+?.,\\^$|#\s]/g; function escapeRegex(value) { return value.replace(reChars, "\\$&"); } /** * Attributes that are case-insensitive in HTML. * * @private * @see https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors */ var caseInsensitiveAttributes = new Set([ "accept", "accept-charset", "align", "alink", "axis", "bgcolor", "charset", "checked", "clear", "codetype", "color", "compact", "declare", "defer", "dir", "direction", "disabled", "enctype", "face", "frame", "hreflang", "http-equiv", "lang", "language", "link", "media", "method", "multiple", "nohref", "noresize", "noshade", "nowrap", "readonly", "rel", "rev", "rules", "scope", "scrolling", "selected", "shape", "target", "text", "type", "valign", "valuetype", "vlink", ]); function shouldIgnoreCase(selector, options) { return typeof selector.ignoreCase === "boolean" ? selector.ignoreCase : selector.ignoreCase === "quirks" ? !!options.quirksMode : !options.xmlMode && caseInsensitiveAttributes.has(selector.name); } /** * Attribute selectors */ exports.attributeRules = { equals: function (next, data, options) { var adapter = options.adapter; var name = data.name; var value = data.value; if (shouldIgnoreCase(data, options)) { value = value.toLowerCase(); return function (elem) { var attr = adapter.getAttributeValue(elem, name); return (attr != null && attr.length === value.length && attr.toLowerCase() === value && next(elem)); }; } return function (elem) { return adapter.getAttributeValue(elem, name) === value && next(elem); }; }, hyphen: function (next, data, options) { var adapter = options.adapter; var name = data.name; var value = data.value; var len = value.length; if (shouldIgnoreCase(data, options)) { value = value.toLowerCase(); return function hyphenIC(elem) { var attr = adapter.getAttributeValue(elem, name); return (attr != null && (attr.length === len || attr.charAt(len) === "-") && attr.substr(0, len).toLowerCase() === value && next(elem)); }; } return function hyphen(elem) { var attr = adapter.getAttributeValue(elem, name); return (attr != null && (attr.length === len || attr.charAt(len) === "-") && attr.substr(0, len) === value && next(elem)); }; }, element: function (next, data, options) { var adapter = options.adapter; var name = data.name, value = data.value; if (/\s/.test(value)) { return boolbase_1.falseFunc; } var regex = new RegExp("(?:^|\\s)".concat(escapeRegex(value), "(?:$|\\s)"), shouldIgnoreCase(data, options) ? "i" : ""); return function element(elem) { var attr = adapter.getAttributeValue(elem, name); return (attr != null && attr.length >= value.length && regex.test(attr) && next(elem)); }; }, exists: function (next, _a, _b) { var name = _a.name; var adapter = _b.adapter; return function (elem) { return adapter.hasAttrib(elem, name) && next(elem); }; }, start: function (next, data, options) { var adapter = options.adapter; var name = data.name; var value = data.value; var len = value.length; if (len === 0) { return boolbase_1.falseFunc; } if (shouldIgnoreCase(data, options)) { value = value.toLowerCase(); return function (elem) { var attr = adapter.getAttributeValue(elem, name); return (attr != null && attr.length >= len && attr.substr(0, len).toLowerCase() === value && next(elem)); }; } return function (elem) { var _a; return !!((_a = adapter.getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.startsWith(value)) && next(elem); }; }, end: function (next, data, options) { var adapter = options.adapter; var name = data.name; var value = data.value; var len = -value.length; if (len === 0) { return boolbase_1.falseFunc; } if (shouldIgnoreCase(data, options)) { value = value.toLowerCase(); return function (elem) { var _a; return ((_a = adapter .getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.substr(len).toLowerCase()) === value && next(elem); }; } return function (elem) { var _a; return !!((_a = adapter.getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.endsWith(value)) && next(elem); }; }, any: function (next, data, options) { var adapter = options.adapter; var name = data.name, value = data.value; if (value === "") { return boolbase_1.falseFunc; } if (shouldIgnoreCase(data, options)) { var regex_1 = new RegExp(escapeRegex(value), "i"); return function anyIC(elem) { var attr = adapter.getAttributeValue(elem, name); return (attr != null && attr.length >= value.length && regex_1.test(attr) && next(elem)); }; } return function (elem) { var _a; return !!((_a = adapter.getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.includes(value)) && next(elem); }; }, not: function (next, data, options) { var adapter = options.adapter; var name = data.name; var value = data.value; if (value === "") { return function (elem) { return !!adapter.getAttributeValue(elem, name) && next(elem); }; } else if (shouldIgnoreCase(data, options)) { value = value.toLowerCase(); return function (elem) { var attr = adapter.getAttributeValue(elem, name); return ((attr == null || attr.length !== value.length || attr.toLowerCase() !== value) && next(elem)); }; } return function (elem) { return adapter.getAttributeValue(elem, name) !== value && next(elem); }; }, }; compile.d.ts 0000644 00000001702 15120140443 0006760 0 ustar 00 import { InternalSelector } from "./types"; import { Selector } from "css-what"; import type { CompiledQuery, InternalOptions } from "./types"; /** * Compiles a selector to an executable function. * * @param selector Selector to compile. * @param options Compilation options. * @param context Optional context for the selector. */ export declare function compile<Node, ElementNode extends Node>(selector: string | Selector[][], options: InternalOptions<Node, ElementNode>, context?: Node[] | Node): CompiledQuery<Node>; export declare function compileUnsafe<Node, ElementNode extends Node>(selector: string | Selector[][], options: InternalOptions<Node, ElementNode>, context?: Node[] | Node): CompiledQuery<ElementNode>; export declare function compileToken<Node, ElementNode extends Node>(token: InternalSelector[][], options: InternalOptions<Node, ElementNode>, context?: Node[] | Node): CompiledQuery<ElementNode>; //# sourceMappingURL=compile.d.ts.map compile.d.ts.map 0000644 00000001556 15120140443 0007543 0 ustar 00 {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAS,QAAQ,EAAgB,MAAM,UAAU,CAAC;AASzD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE9D;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EAClD,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,EAC/B,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,GACxB,aAAa,CAAC,IAAI,CAAC,CAGrB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EACxD,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,EAC/B,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,GACxB,aAAa,CAAC,WAAW,CAAC,CAG5B;AAiDD,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EACvD,KAAK,EAAE,gBAAgB,EAAE,EAAE,EAC3B,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,GACxB,aAAa,CAAC,WAAW,CAAC,CA2C5B"} compile.js 0000644 00000010702 15120140443 0006524 0 ustar 00 "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.compileToken = exports.compileUnsafe = exports.compile = void 0; var css_what_1 = require("css-what"); var boolbase_1 = require("boolbase"); var sort_1 = __importDefault(require("./sort")); var procedure_1 = require("./procedure"); var general_1 = require("./general"); var subselects_1 = require("./pseudo-selectors/subselects"); /** * Compiles a selector to an executable function. * * @param selector Selector to compile. * @param options Compilation options. * @param context Optional context for the selector. */ function compile(selector, options, context) { var next = compileUnsafe(selector, options, context); return (0, subselects_1.ensureIsTag)(next, options.adapter); } exports.compile = compile; function compileUnsafe(selector, options, context) { var token = typeof selector === "string" ? (0, css_what_1.parse)(selector) : selector; return compileToken(token, options, context); } exports.compileUnsafe = compileUnsafe; function includesScopePseudo(t) { return (t.type === "pseudo" && (t.name === "scope" || (Array.isArray(t.data) && t.data.some(function (data) { return data.some(includesScopePseudo); })))); } var DESCENDANT_TOKEN = { type: css_what_1.SelectorType.Descendant }; var FLEXIBLE_DESCENDANT_TOKEN = { type: "_flexibleDescendant", }; var SCOPE_TOKEN = { type: css_what_1.SelectorType.Pseudo, name: "scope", data: null, }; /* * CSS 4 Spec (Draft): 3.3.1. Absolutizing a Scope-relative Selector * http://www.w3.org/TR/selectors4/#absolutizing */ function absolutize(token, _a, context) { var adapter = _a.adapter; // TODO Use better check if the context is a document var hasContext = !!(context === null || context === void 0 ? void 0 : context.every(function (e) { var parent = adapter.isTag(e) && adapter.getParent(e); return e === subselects_1.PLACEHOLDER_ELEMENT || (parent && adapter.isTag(parent)); })); for (var _i = 0, token_1 = token; _i < token_1.length; _i++) { var t = token_1[_i]; if (t.length > 0 && (0, procedure_1.isTraversal)(t[0]) && t[0].type !== "descendant") { // Don't continue in else branch } else if (hasContext && !t.some(includesScopePseudo)) { t.unshift(DESCENDANT_TOKEN); } else { continue; } t.unshift(SCOPE_TOKEN); } } function compileToken(token, options, context) { var _a; token = token.filter(function (t) { return t.length > 0; }); token.forEach(sort_1.default); context = (_a = options.context) !== null && _a !== void 0 ? _a : context; var isArrayContext = Array.isArray(context); var finalContext = context && (Array.isArray(context) ? context : [context]); absolutize(token, options, finalContext); var shouldTestNextSiblings = false; var query = token .map(function (rules) { if (rules.length >= 2) { var first = rules[0], second = rules[1]; if (first.type !== "pseudo" || first.name !== "scope") { // Ignore } else if (isArrayContext && second.type === "descendant") { rules[1] = FLEXIBLE_DESCENDANT_TOKEN; } else if (second.type === "adjacent" || second.type === "sibling") { shouldTestNextSiblings = true; } } return compileRules(rules, options, finalContext); }) .reduce(reduceRules, boolbase_1.falseFunc); query.shouldTestNextSiblings = shouldTestNextSiblings; return query; } exports.compileToken = compileToken; function compileRules(rules, options, context) { var _a; return rules.reduce(function (previous, rule) { return previous === boolbase_1.falseFunc ? boolbase_1.falseFunc : (0, general_1.compileGeneralSelector)(previous, rule, options, context, compileToken); }, (_a = options.rootFunc) !== null && _a !== void 0 ? _a : boolbase_1.trueFunc); } function reduceRules(a, b) { if (b === boolbase_1.falseFunc || a === boolbase_1.trueFunc) { return a; } if (a === boolbase_1.falseFunc || b === boolbase_1.trueFunc) { return b; } return function combine(elem) { return a(elem) || b(elem); }; } general.d.ts 0000644 00000000647 15120140443 0006754 0 ustar 00 import type { CompiledQuery, InternalOptions, InternalSelector, CompileToken } from "./types"; export declare function compileGeneralSelector<Node, ElementNode extends Node>(next: CompiledQuery<ElementNode>, selector: InternalSelector, options: InternalOptions<Node, ElementNode>, context: Node[] | undefined, compileToken: CompileToken<Node, ElementNode>): CompiledQuery<ElementNode>; //# sourceMappingURL=general.d.ts.map general.d.ts.map 0000644 00000000707 15120140443 0007525 0 ustar 00 {"version":3,"file":"general.d.ts","sourceRoot":"","sources":["../src/general.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACR,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,YAAY,EACf,MAAM,SAAS,CAAC;AAOjB,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EACjE,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,EAChC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,EAAE,IAAI,EAAE,GAAG,SAAS,EAC3B,YAAY,EAAE,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,GAC9C,aAAa,CAAC,WAAW,CAAC,CAiK5B"} general.js 0000644 00000013264 15120140443 0006517 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.compileGeneralSelector = void 0; var attributes_1 = require("./attributes"); var pseudo_selectors_1 = require("./pseudo-selectors"); var css_what_1 = require("css-what"); /* * All available rules */ function compileGeneralSelector(next, selector, options, context, compileToken) { var adapter = options.adapter, equals = options.equals; switch (selector.type) { case css_what_1.SelectorType.PseudoElement: { throw new Error("Pseudo-elements are not supported by css-select"); } case css_what_1.SelectorType.ColumnCombinator: { throw new Error("Column combinators are not yet supported by css-select"); } case css_what_1.SelectorType.Attribute: { if (selector.namespace != null) { throw new Error("Namespaced attributes are not yet supported by css-select"); } if (!options.xmlMode || options.lowerCaseAttributeNames) { selector.name = selector.name.toLowerCase(); } return attributes_1.attributeRules[selector.action](next, selector, options); } case css_what_1.SelectorType.Pseudo: { return (0, pseudo_selectors_1.compilePseudoSelector)(next, selector, options, context, compileToken); } // Tags case css_what_1.SelectorType.Tag: { if (selector.namespace != null) { throw new Error("Namespaced tag names are not yet supported by css-select"); } var name_1 = selector.name; if (!options.xmlMode || options.lowerCaseTags) { name_1 = name_1.toLowerCase(); } return function tag(elem) { return adapter.getName(elem) === name_1 && next(elem); }; } // Traversal case css_what_1.SelectorType.Descendant: { if (options.cacheResults === false || typeof WeakSet === "undefined") { return function descendant(elem) { var current = elem; while ((current = adapter.getParent(current))) { if (adapter.isTag(current) && next(current)) { return true; } } return false; }; } // @ts-expect-error `ElementNode` is not extending object var isFalseCache_1 = new WeakSet(); return function cachedDescendant(elem) { var current = elem; while ((current = adapter.getParent(current))) { if (!isFalseCache_1.has(current)) { if (adapter.isTag(current) && next(current)) { return true; } isFalseCache_1.add(current); } } return false; }; } case "_flexibleDescendant": { // Include element itself, only used while querying an array return function flexibleDescendant(elem) { var current = elem; do { if (adapter.isTag(current) && next(current)) return true; } while ((current = adapter.getParent(current))); return false; }; } case css_what_1.SelectorType.Parent: { return function parent(elem) { return adapter .getChildren(elem) .some(function (elem) { return adapter.isTag(elem) && next(elem); }); }; } case css_what_1.SelectorType.Child: { return function child(elem) { var parent = adapter.getParent(elem); return parent != null && adapter.isTag(parent) && next(parent); }; } case css_what_1.SelectorType.Sibling: { return function sibling(elem) { var siblings = adapter.getSiblings(elem); for (var i = 0; i < siblings.length; i++) { var currentSibling = siblings[i]; if (equals(elem, currentSibling)) break; if (adapter.isTag(currentSibling) && next(currentSibling)) { return true; } } return false; }; } case css_what_1.SelectorType.Adjacent: { if (adapter.prevElementSibling) { return function adjacent(elem) { var previous = adapter.prevElementSibling(elem); return previous != null && next(previous); }; } return function adjacent(elem) { var siblings = adapter.getSiblings(elem); var lastElement; for (var i = 0; i < siblings.length; i++) { var currentSibling = siblings[i]; if (equals(elem, currentSibling)) break; if (adapter.isTag(currentSibling)) { lastElement = currentSibling; } } return !!lastElement && next(lastElement); }; } case css_what_1.SelectorType.Universal: { if (selector.namespace != null && selector.namespace !== "*") { throw new Error("Namespaced universal selectors are not yet supported by css-select"); } return next; } } } exports.compileGeneralSelector = compileGeneralSelector; index.d.ts 0000644 00000006323 15120140443 0006443 0 ustar 00 import type { CompiledQuery, Options, Query, Adapter } from "./types"; export type { Options }; /** * Compiles the query, returns a function. */ export declare const compile: <Node_1, ElementNode extends Node_1>(selector: string | import("css-what").Selector[][], options?: Options<Node_1, ElementNode> | undefined, context?: Node_1 | Node_1[] | undefined) => CompiledQuery<Node_1>; export declare const _compileUnsafe: <Node_1, ElementNode extends Node_1>(selector: string | import("css-what").Selector[][], options?: Options<Node_1, ElementNode> | undefined, context?: Node_1 | Node_1[] | undefined) => CompiledQuery<ElementNode>; export declare const _compileToken: <Node_1, ElementNode extends Node_1>(selector: import("./types").InternalSelector[][], options?: Options<Node_1, ElementNode> | undefined, context?: Node_1 | Node_1[] | undefined) => CompiledQuery<ElementNode>; export declare function prepareContext<Node, ElementNode extends Node>(elems: Node | Node[], adapter: Adapter<Node, ElementNode>, shouldTestNextSiblings?: boolean): Node[]; /** * @template Node The generic Node type for the DOM adapter being used. * @template ElementNode The Node type for elements for the DOM adapter being used. * @param elems Elements to query. If it is an element, its children will be queried.. * @param query can be either a CSS selector string or a compiled query function. * @param [options] options for querying the document. * @see compile for supported selector queries. * @returns All matching elements. * */ export declare const selectAll: <Node_1, ElementNode extends Node_1>(query: Query<ElementNode>, elements: Node_1 | Node_1[], options?: Options<Node_1, ElementNode> | undefined) => ElementNode[]; /** * @template Node The generic Node type for the DOM adapter being used. * @template ElementNode The Node type for elements for the DOM adapter being used. * @param elems Elements to query. If it is an element, its children will be queried.. * @param query can be either a CSS selector string or a compiled query function. * @param [options] options for querying the document. * @see compile for supported selector queries. * @returns the first match, or null if there was no match. */ export declare const selectOne: <Node_1, ElementNode extends Node_1>(query: Query<ElementNode>, elements: Node_1 | Node_1[], options?: Options<Node_1, ElementNode> | undefined) => ElementNode | null; /** * Tests whether or not an element is matched by query. * * @template Node The generic Node type for the DOM adapter being used. * @template ElementNode The Node type for elements for the DOM adapter being used. * @param elem The element to test if it matches the query. * @param query can be either a CSS selector string or a compiled query function. * @param [options] options for querying the document. * @see compile for supported selector queries. * @returns */ export declare function is<Node, ElementNode extends Node>(elem: ElementNode, query: Query<ElementNode>, options?: Options<Node, ElementNode>): boolean; /** * Alias for selectAll(query, elems, options). * @see [compile] for supported selector queries. */ export default selectAll; export { filters, pseudos, aliases } from "./pseudo-selectors"; //# sourceMappingURL=index.d.ts.map index.d.ts.map 0000644 00000001553 15120140443 0007217 0 ustar 00 {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACR,aAAa,EACb,OAAO,EAEP,KAAK,EACL,OAAO,EAEV,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,OAAO,EAAE,CAAC;AA0CxB;;GAEG;AACH,eAAO,MAAM,OAAO,gNAA0B,CAAC;AAC/C,eAAO,MAAM,cAAc,qNAA6B,CAAC;AACzD,eAAO,MAAM,aAAa,mNAA4B,CAAC;AA6BvD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EACzD,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,EACpB,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,EACnC,sBAAsB,UAAQ,GAC/B,IAAI,EAAE,CAYR;AAiBD;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,mKASrB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,wKASrB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,EAAE,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EAC7C,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,EACzB,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GACrC,OAAO,CAKT;AAED;;;GAGG;AACH,eAAe,SAAS,CAAC;AAGzB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC"} index.js 0000644 00000015304 15120140443 0006206 0 ustar 00 "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.aliases = exports.pseudos = exports.filters = exports.is = exports.selectOne = exports.selectAll = exports.prepareContext = exports._compileToken = exports._compileUnsafe = exports.compile = void 0; var DomUtils = __importStar(require("domutils")); var boolbase_1 = require("boolbase"); var compile_1 = require("./compile"); var subselects_1 = require("./pseudo-selectors/subselects"); var defaultEquals = function (a, b) { return a === b; }; var defaultOptions = { adapter: DomUtils, equals: defaultEquals, }; function convertOptionFormats(options) { var _a, _b, _c, _d; /* * We force one format of options to the other one. */ // @ts-expect-error Default options may have incompatible `Node` / `ElementNode`. var opts = options !== null && options !== void 0 ? options : defaultOptions; // @ts-expect-error Same as above. (_a = opts.adapter) !== null && _a !== void 0 ? _a : (opts.adapter = DomUtils); // @ts-expect-error `equals` does not exist on `Options` (_b = opts.equals) !== null && _b !== void 0 ? _b : (opts.equals = (_d = (_c = opts.adapter) === null || _c === void 0 ? void 0 : _c.equals) !== null && _d !== void 0 ? _d : defaultEquals); return opts; } function wrapCompile(func) { return function addAdapter(selector, options, context) { var opts = convertOptionFormats(options); return func(selector, opts, context); }; } /** * Compiles the query, returns a function. */ exports.compile = wrapCompile(compile_1.compile); exports._compileUnsafe = wrapCompile(compile_1.compileUnsafe); exports._compileToken = wrapCompile(compile_1.compileToken); function getSelectorFunc(searchFunc) { return function select(query, elements, options) { var opts = convertOptionFormats(options); if (typeof query !== "function") { query = (0, compile_1.compileUnsafe)(query, opts, elements); } var filteredElements = prepareContext(elements, opts.adapter, query.shouldTestNextSiblings); return searchFunc(query, filteredElements, opts); }; } function prepareContext(elems, adapter, shouldTestNextSiblings) { if (shouldTestNextSiblings === void 0) { shouldTestNextSiblings = false; } /* * Add siblings if the query requires them. * See https://github.com/fb55/css-select/pull/43#issuecomment-225414692 */ if (shouldTestNextSiblings) { elems = appendNextSiblings(elems, adapter); } return Array.isArray(elems) ? adapter.removeSubsets(elems) : adapter.getChildren(elems); } exports.prepareContext = prepareContext; function appendNextSiblings(elem, adapter) { // Order matters because jQuery seems to check the children before the siblings var elems = Array.isArray(elem) ? elem.slice(0) : [elem]; var elemsLength = elems.length; for (var i = 0; i < elemsLength; i++) { var nextSiblings = (0, subselects_1.getNextSiblings)(elems[i], adapter); elems.push.apply(elems, nextSiblings); } return elems; } /** * @template Node The generic Node type for the DOM adapter being used. * @template ElementNode The Node type for elements for the DOM adapter being used. * @param elems Elements to query. If it is an element, its children will be queried.. * @param query can be either a CSS selector string or a compiled query function. * @param [options] options for querying the document. * @see compile for supported selector queries. * @returns All matching elements. * */ exports.selectAll = getSelectorFunc(function (query, elems, options) { return query === boolbase_1.falseFunc || !elems || elems.length === 0 ? [] : options.adapter.findAll(query, elems); }); /** * @template Node The generic Node type for the DOM adapter being used. * @template ElementNode The Node type for elements for the DOM adapter being used. * @param elems Elements to query. If it is an element, its children will be queried.. * @param query can be either a CSS selector string or a compiled query function. * @param [options] options for querying the document. * @see compile for supported selector queries. * @returns the first match, or null if there was no match. */ exports.selectOne = getSelectorFunc(function (query, elems, options) { return query === boolbase_1.falseFunc || !elems || elems.length === 0 ? null : options.adapter.findOne(query, elems); }); /** * Tests whether or not an element is matched by query. * * @template Node The generic Node type for the DOM adapter being used. * @template ElementNode The Node type for elements for the DOM adapter being used. * @param elem The element to test if it matches the query. * @param query can be either a CSS selector string or a compiled query function. * @param [options] options for querying the document. * @see compile for supported selector queries. * @returns */ function is(elem, query, options) { var opts = convertOptionFormats(options); return (typeof query === "function" ? query : (0, compile_1.compile)(query, opts))(elem); } exports.is = is; /** * Alias for selectAll(query, elems, options). * @see [compile] for supported selector queries. */ exports.default = exports.selectAll; // Export filters, pseudos and aliases to allow users to supply their own. var pseudo_selectors_1 = require("./pseudo-selectors"); Object.defineProperty(exports, "filters", { enumerable: true, get: function () { return pseudo_selectors_1.filters; } }); Object.defineProperty(exports, "pseudos", { enumerable: true, get: function () { return pseudo_selectors_1.pseudos; } }); Object.defineProperty(exports, "aliases", { enumerable: true, get: function () { return pseudo_selectors_1.aliases; } }); procedure.d.ts 0000644 00000000427 15120140443 0007323 0 ustar 00 import type { Traversal } from "css-what"; import type { InternalSelector } from "./types"; export declare const procedure: Record<InternalSelector["type"], number>; export declare function isTraversal(t: InternalSelector): t is Traversal; //# sourceMappingURL=procedure.d.ts.map procedure.d.ts.map 0000644 00000000534 15120140443 0010076 0 ustar 00 {"version":3,"file":"procedure.d.ts","sourceRoot":"","sources":["../src/procedure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,CAa9D,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,GAAG,CAAC,IAAI,SAAS,CAE/D"} procedure.js 0000644 00000000753 15120140443 0007071 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isTraversal = exports.procedure = void 0; exports.procedure = { universal: 50, tag: 30, attribute: 1, pseudo: 0, "pseudo-element": 0, "column-combinator": -1, descendant: -1, child: -1, parent: -1, sibling: -1, adjacent: -1, _flexibleDescendant: -1, }; function isTraversal(t) { return exports.procedure[t.type] < 0; } exports.isTraversal = isTraversal; pseudo-selectors/aliases.d.ts 0000644 00000000233 15120140443 0012247 0 ustar 00 /** * Aliases are pseudos that are expressed as selectors. */ export declare const aliases: Record<string, string>; //# sourceMappingURL=aliases.d.ts.map pseudo-selectors/aliases.d.ts.map 0000644 00000000310 15120140443 0013017 0 ustar 00 {"version":3,"file":"aliases.d.ts","sourceRoot":"","sources":["../../src/pseudo-selectors/aliases.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwC1C,CAAC"} pseudo-selectors/aliases.js 0000644 00000002716 15120140443 0012023 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.aliases = void 0; /** * Aliases are pseudos that are expressed as selectors. */ exports.aliases = { // Links "any-link": ":is(a, area, link)[href]", link: ":any-link:not(:visited)", // Forms // https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements disabled: ":is(\n :is(button, input, select, textarea, optgroup, option)[disabled],\n optgroup[disabled] > option,\n fieldset[disabled]:not(fieldset[disabled] legend:first-of-type *)\n )", enabled: ":not(:disabled)", checked: ":is(:is(input[type=radio], input[type=checkbox])[checked], option:selected)", required: ":is(input, select, textarea)[required]", optional: ":is(input, select, textarea):not([required])", // JQuery extensions // https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-selectedness selected: "option:is([selected], select:not([multiple]):not(:has(> option[selected])) > :first-of-type)", checkbox: "[type=checkbox]", file: "[type=file]", password: "[type=password]", radio: "[type=radio]", reset: "[type=reset]", image: "[type=image]", submit: "[type=submit]", parent: ":not(:empty)", header: ":is(h1, h2, h3, h4, h5, h6)", button: ":is(button, input[type=button])", input: ":is(input, textarea, select, button)", text: "input:is(:not([type!='']), [type=text])", }; pseudo-selectors/filters.d.ts 0000644 00000000547 15120140443 0012306 0 ustar 00 import type { CompiledQuery, InternalOptions } from "../types"; export declare type Filter = <Node, ElementNode extends Node>(next: CompiledQuery<ElementNode>, text: string, options: InternalOptions<Node, ElementNode>, context?: Node[]) => CompiledQuery<ElementNode>; export declare const filters: Record<string, Filter>; //# sourceMappingURL=filters.d.ts.map pseudo-selectors/filters.d.ts.map 0000644 00000000714 15120140443 0013056 0 ustar 00 {"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../../src/pseudo-selectors/filters.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAW,MAAM,UAAU,CAAC;AAExE,oBAAY,MAAM,GAAG,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EAChD,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,EAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,CAAC,EAAE,IAAI,EAAE,KACf,aAAa,CAAC,WAAW,CAAC,CAAC;AAYhC,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA2I1C,CAAC"} pseudo-selectors/filters.js 0000644 00000013352 15120140443 0012050 0 ustar 00 "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.filters = void 0; var nth_check_1 = __importDefault(require("nth-check")); var boolbase_1 = require("boolbase"); function getChildFunc(next, adapter) { return function (elem) { var parent = adapter.getParent(elem); return parent != null && adapter.isTag(parent) && next(elem); }; } exports.filters = { contains: function (next, text, _a) { var adapter = _a.adapter; return function contains(elem) { return next(elem) && adapter.getText(elem).includes(text); }; }, icontains: function (next, text, _a) { var adapter = _a.adapter; var itext = text.toLowerCase(); return function icontains(elem) { return (next(elem) && adapter.getText(elem).toLowerCase().includes(itext)); }; }, // Location specific methods "nth-child": function (next, rule, _a) { var adapter = _a.adapter, equals = _a.equals; var func = (0, nth_check_1.default)(rule); if (func === boolbase_1.falseFunc) return boolbase_1.falseFunc; if (func === boolbase_1.trueFunc) return getChildFunc(next, adapter); return function nthChild(elem) { var siblings = adapter.getSiblings(elem); var pos = 0; for (var i = 0; i < siblings.length; i++) { if (equals(elem, siblings[i])) break; if (adapter.isTag(siblings[i])) { pos++; } } return func(pos) && next(elem); }; }, "nth-last-child": function (next, rule, _a) { var adapter = _a.adapter, equals = _a.equals; var func = (0, nth_check_1.default)(rule); if (func === boolbase_1.falseFunc) return boolbase_1.falseFunc; if (func === boolbase_1.trueFunc) return getChildFunc(next, adapter); return function nthLastChild(elem) { var siblings = adapter.getSiblings(elem); var pos = 0; for (var i = siblings.length - 1; i >= 0; i--) { if (equals(elem, siblings[i])) break; if (adapter.isTag(siblings[i])) { pos++; } } return func(pos) && next(elem); }; }, "nth-of-type": function (next, rule, _a) { var adapter = _a.adapter, equals = _a.equals; var func = (0, nth_check_1.default)(rule); if (func === boolbase_1.falseFunc) return boolbase_1.falseFunc; if (func === boolbase_1.trueFunc) return getChildFunc(next, adapter); return function nthOfType(elem) { var siblings = adapter.getSiblings(elem); var pos = 0; for (var i = 0; i < siblings.length; i++) { var currentSibling = siblings[i]; if (equals(elem, currentSibling)) break; if (adapter.isTag(currentSibling) && adapter.getName(currentSibling) === adapter.getName(elem)) { pos++; } } return func(pos) && next(elem); }; }, "nth-last-of-type": function (next, rule, _a) { var adapter = _a.adapter, equals = _a.equals; var func = (0, nth_check_1.default)(rule); if (func === boolbase_1.falseFunc) return boolbase_1.falseFunc; if (func === boolbase_1.trueFunc) return getChildFunc(next, adapter); return function nthLastOfType(elem) { var siblings = adapter.getSiblings(elem); var pos = 0; for (var i = siblings.length - 1; i >= 0; i--) { var currentSibling = siblings[i]; if (equals(elem, currentSibling)) break; if (adapter.isTag(currentSibling) && adapter.getName(currentSibling) === adapter.getName(elem)) { pos++; } } return func(pos) && next(elem); }; }, // TODO determine the actual root element root: function (next, _rule, _a) { var adapter = _a.adapter; return function (elem) { var parent = adapter.getParent(elem); return (parent == null || !adapter.isTag(parent)) && next(elem); }; }, scope: function (next, rule, options, context) { var equals = options.equals; if (!context || context.length === 0) { // Equivalent to :root return exports.filters.root(next, rule, options); } if (context.length === 1) { // NOTE: can't be unpacked, as :has uses this for side-effects return function (elem) { return equals(context[0], elem) && next(elem); }; } return function (elem) { return context.includes(elem) && next(elem); }; }, hover: dynamicStatePseudo("isHovered"), visited: dynamicStatePseudo("isVisited"), active: dynamicStatePseudo("isActive"), }; /** * Dynamic state pseudos. These depend on optional Adapter methods. * * @param name The name of the adapter method to call. * @returns Pseudo for the `filters` object. */ function dynamicStatePseudo(name) { return function dynamicPseudo(next, _rule, _a) { var adapter = _a.adapter; var func = adapter[name]; if (typeof func !== "function") { return boolbase_1.falseFunc; } return function active(elem) { return func(elem) && next(elem); }; }; } pseudo-selectors/index.d.ts 0000644 00000001121 15120140443 0011732 0 ustar 00 import type { CompiledQuery, InternalOptions, CompileToken } from "../types"; import { PseudoSelector } from "css-what"; import { filters } from "./filters"; import { pseudos } from "./pseudos"; import { aliases } from "./aliases"; export { filters, pseudos, aliases }; export declare function compilePseudoSelector<Node, ElementNode extends Node>(next: CompiledQuery<ElementNode>, selector: PseudoSelector, options: InternalOptions<Node, ElementNode>, context: Node[] | undefined, compileToken: CompileToken<Node, ElementNode>): CompiledQuery<ElementNode>; //# sourceMappingURL=index.d.ts.map pseudo-selectors/index.d.ts.map 0000644 00000001240 15120140443 0012510 0 ustar 00 {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pseudo-selectors/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAS,cAAc,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAoB,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAErC,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EAChE,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,EAChC,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,EAAE,IAAI,EAAE,GAAG,SAAS,EAC3B,YAAY,EAAE,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,GAC9C,aAAa,CAAC,WAAW,CAAC,CA6B5B"} pseudo-selectors/index.js 0000644 00000005127 15120140443 0011510 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.compilePseudoSelector = exports.aliases = exports.pseudos = exports.filters = void 0; /* * Pseudo selectors * * Pseudo selectors are available in three forms: * * 1. Filters are called when the selector is compiled and return a function * that has to return either false, or the results of `next()`. * 2. Pseudos are called on execution. They have to return a boolean. * 3. Subselects work like filters, but have an embedded selector that will be run separately. * * Filters are great if you want to do some pre-processing, or change the call order * of `next()` and your code. * Pseudos should be used to implement simple checks. */ var boolbase_1 = require("boolbase"); var css_what_1 = require("css-what"); var filters_1 = require("./filters"); Object.defineProperty(exports, "filters", { enumerable: true, get: function () { return filters_1.filters; } }); var pseudos_1 = require("./pseudos"); Object.defineProperty(exports, "pseudos", { enumerable: true, get: function () { return pseudos_1.pseudos; } }); var aliases_1 = require("./aliases"); Object.defineProperty(exports, "aliases", { enumerable: true, get: function () { return aliases_1.aliases; } }); var subselects_1 = require("./subselects"); function compilePseudoSelector(next, selector, options, context, compileToken) { var name = selector.name, data = selector.data; if (Array.isArray(data)) { return subselects_1.subselects[name](next, data, options, context, compileToken); } if (name in aliases_1.aliases) { if (data != null) { throw new Error("Pseudo ".concat(name, " doesn't have any arguments")); } // The alias has to be parsed here, to make sure options are respected. var alias = (0, css_what_1.parse)(aliases_1.aliases[name]); return subselects_1.subselects.is(next, alias, options, context, compileToken); } if (name in filters_1.filters) { return filters_1.filters[name](next, data, options, context); } if (name in pseudos_1.pseudos) { var pseudo_1 = pseudos_1.pseudos[name]; (0, pseudos_1.verifyPseudoArgs)(pseudo_1, name, data); return pseudo_1 === boolbase_1.falseFunc ? boolbase_1.falseFunc : next === boolbase_1.trueFunc ? function (elem) { return pseudo_1(elem, options, data); } : function (elem) { return pseudo_1(elem, options, data) && next(elem); }; } throw new Error("unmatched pseudo-class :".concat(name)); } exports.compilePseudoSelector = compilePseudoSelector; pseudo-selectors/pseudos.d.ts 0000644 00000000731 15120140443 0012313 0 ustar 00 import { PseudoSelector } from "css-what"; import type { InternalOptions } from "../types"; export declare type Pseudo = <Node, ElementNode extends Node>(elem: ElementNode, options: InternalOptions<Node, ElementNode>, subselect?: ElementNode | string | null) => boolean; export declare const pseudos: Record<string, Pseudo>; export declare function verifyPseudoArgs(func: Pseudo, name: string, subselect: PseudoSelector["data"]): void; //# sourceMappingURL=pseudos.d.ts.map pseudo-selectors/pseudos.d.ts.map 0000644 00000001067 15120140443 0013072 0 ustar 00 {"version":3,"file":"pseudos.d.ts","sourceRoot":"","sources":["../../src/pseudo-selectors/pseudos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,oBAAY,MAAM,GAAG,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EAChD,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,SAAS,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,KACtC,OAAO,CAAC;AAGb,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA8E1C,CAAC;AAEF,wBAAgB,gBAAgB,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,cAAc,CAAC,MAAM,CAAC,GAClC,IAAI,CAQN"} pseudo-selectors/pseudos.js 0000644 00000006425 15120140443 0012065 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.verifyPseudoArgs = exports.pseudos = void 0; // While filters are precompiled, pseudos get called when they are needed exports.pseudos = { empty: function (elem, _a) { var adapter = _a.adapter; return !adapter.getChildren(elem).some(function (elem) { // FIXME: `getText` call is potentially expensive. return adapter.isTag(elem) || adapter.getText(elem) !== ""; }); }, "first-child": function (elem, _a) { var adapter = _a.adapter, equals = _a.equals; var firstChild = adapter .getSiblings(elem) .find(function (elem) { return adapter.isTag(elem); }); return firstChild != null && equals(elem, firstChild); }, "last-child": function (elem, _a) { var adapter = _a.adapter, equals = _a.equals; var siblings = adapter.getSiblings(elem); for (var i = siblings.length - 1; i >= 0; i--) { if (equals(elem, siblings[i])) return true; if (adapter.isTag(siblings[i])) break; } return false; }, "first-of-type": function (elem, _a) { var adapter = _a.adapter, equals = _a.equals; var siblings = adapter.getSiblings(elem); var elemName = adapter.getName(elem); for (var i = 0; i < siblings.length; i++) { var currentSibling = siblings[i]; if (equals(elem, currentSibling)) return true; if (adapter.isTag(currentSibling) && adapter.getName(currentSibling) === elemName) { break; } } return false; }, "last-of-type": function (elem, _a) { var adapter = _a.adapter, equals = _a.equals; var siblings = adapter.getSiblings(elem); var elemName = adapter.getName(elem); for (var i = siblings.length - 1; i >= 0; i--) { var currentSibling = siblings[i]; if (equals(elem, currentSibling)) return true; if (adapter.isTag(currentSibling) && adapter.getName(currentSibling) === elemName) { break; } } return false; }, "only-of-type": function (elem, _a) { var adapter = _a.adapter, equals = _a.equals; var elemName = adapter.getName(elem); return adapter .getSiblings(elem) .every(function (sibling) { return equals(elem, sibling) || !adapter.isTag(sibling) || adapter.getName(sibling) !== elemName; }); }, "only-child": function (elem, _a) { var adapter = _a.adapter, equals = _a.equals; return adapter .getSiblings(elem) .every(function (sibling) { return equals(elem, sibling) || !adapter.isTag(sibling); }); }, }; function verifyPseudoArgs(func, name, subselect) { if (subselect === null) { if (func.length > 2) { throw new Error("pseudo-selector :".concat(name, " requires an argument")); } } else if (func.length === 2) { throw new Error("pseudo-selector :".concat(name, " doesn't have any arguments")); } } exports.verifyPseudoArgs = verifyPseudoArgs; pseudo-selectors/subselects.d.ts 0000644 00000001677 15120140443 0013017 0 ustar 00 import { CompileToken } from "./../types"; import type { Selector } from "css-what"; import type { CompiledQuery, InternalOptions, Adapter } from "../types"; /** Used as a placeholder for :has. Will be replaced with the actual element. */ export declare const PLACEHOLDER_ELEMENT: {}; export declare function ensureIsTag<Node, ElementNode extends Node>(next: CompiledQuery<ElementNode>, adapter: Adapter<Node, ElementNode>): CompiledQuery<Node>; export declare type Subselect = <Node, ElementNode extends Node>(next: CompiledQuery<ElementNode>, subselect: Selector[][], options: InternalOptions<Node, ElementNode>, context: Node[] | undefined, compileToken: CompileToken<Node, ElementNode>) => CompiledQuery<ElementNode>; export declare function getNextSiblings<Node, ElementNode extends Node>(elem: Node, adapter: Adapter<Node, ElementNode>): ElementNode[]; export declare const subselects: Record<string, Subselect>; //# sourceMappingURL=subselects.d.ts.map pseudo-selectors/subselects.d.ts.map 0000644 00000001710 15120140443 0013557 0 ustar 00 {"version":3,"file":"subselects.d.ts","sourceRoot":"","sources":["../../src/pseudo-selectors/subselects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGxE,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,IAAK,CAAC;AAEtC,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EACtD,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,EAChC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GACpC,aAAa,CAAC,IAAI,CAAC,CAGrB;AAED,oBAAY,SAAS,GAAG,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EACnD,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,EAChC,SAAS,EAAE,QAAQ,EAAE,EAAE,EACvB,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,EAAE,IAAI,EAAE,GAAG,SAAS,EAC3B,YAAY,EAAE,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,KAC5C,aAAa,CAAC,WAAW,CAAC,CAAC;AAEhC,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,EAC1D,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GACpC,WAAW,EAAE,CAMf;AAkBD,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CA6EhD,CAAC"} pseudo-selectors/subselects.js 0000644 00000010316 15120140443 0012551 0 ustar 00 "use strict"; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.subselects = exports.getNextSiblings = exports.ensureIsTag = exports.PLACEHOLDER_ELEMENT = void 0; var boolbase_1 = require("boolbase"); var procedure_1 = require("../procedure"); /** Used as a placeholder for :has. Will be replaced with the actual element. */ exports.PLACEHOLDER_ELEMENT = {}; function ensureIsTag(next, adapter) { if (next === boolbase_1.falseFunc) return boolbase_1.falseFunc; return function (elem) { return adapter.isTag(elem) && next(elem); }; } exports.ensureIsTag = ensureIsTag; function getNextSiblings(elem, adapter) { var siblings = adapter.getSiblings(elem); if (siblings.length <= 1) return []; var elemIndex = siblings.indexOf(elem); if (elemIndex < 0 || elemIndex === siblings.length - 1) return []; return siblings.slice(elemIndex + 1).filter(adapter.isTag); } exports.getNextSiblings = getNextSiblings; var is = function (next, token, options, context, compileToken) { var opts = { xmlMode: !!options.xmlMode, adapter: options.adapter, equals: options.equals, }; var func = compileToken(token, opts, context); return function (elem) { return func(elem) && next(elem); }; }; /* * :not, :has, :is, :matches and :where have to compile selectors * doing this in src/pseudos.ts would lead to circular dependencies, * so we add them here */ exports.subselects = { is: is, /** * `:matches` and `:where` are aliases for `:is`. */ matches: is, where: is, not: function (next, token, options, context, compileToken) { var opts = { xmlMode: !!options.xmlMode, adapter: options.adapter, equals: options.equals, }; var func = compileToken(token, opts, context); if (func === boolbase_1.falseFunc) return next; if (func === boolbase_1.trueFunc) return boolbase_1.falseFunc; return function not(elem) { return !func(elem) && next(elem); }; }, has: function (next, subselect, options, _context, compileToken) { var adapter = options.adapter; var opts = { xmlMode: !!options.xmlMode, adapter: adapter, equals: options.equals, }; // @ts-expect-error Uses an array as a pointer to the current element (side effects) var context = subselect.some(function (s) { return s.some(procedure_1.isTraversal); }) ? [exports.PLACEHOLDER_ELEMENT] : undefined; var compiled = compileToken(subselect, opts, context); if (compiled === boolbase_1.falseFunc) return boolbase_1.falseFunc; if (compiled === boolbase_1.trueFunc) { return function (elem) { return adapter.getChildren(elem).some(adapter.isTag) && next(elem); }; } var hasElement = ensureIsTag(compiled, adapter); var _a = compiled.shouldTestNextSiblings, shouldTestNextSiblings = _a === void 0 ? false : _a; /* * `shouldTestNextSiblings` will only be true if the query starts with * a traversal (sibling or adjacent). That means we will always have a context. */ if (context) { return function (elem) { context[0] = elem; var childs = adapter.getChildren(elem); var nextElements = shouldTestNextSiblings ? __spreadArray(__spreadArray([], childs, true), getNextSiblings(elem, adapter), true) : childs; return (next(elem) && adapter.existsOne(hasElement, nextElements)); }; } return function (elem) { return next(elem) && adapter.existsOne(hasElement, adapter.getChildren(elem)); }; }, }; sort.d.ts 0000644 00000000515 15120140443 0006320 0 ustar 00 import type { InternalSelector } from "./types"; /** * Sort the parts of the passed selector, * as there is potential for optimization * (some types of selectors are faster than others) * * @param arr Selector to sort */ export default function sortByProcedure(arr: InternalSelector[]): void; //# sourceMappingURL=sort.d.ts.map sort.d.ts.map 0000644 00000000355 15120140443 0007076 0 ustar 00 {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../src/sort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAehD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAerE"} sort.js 0000644 00000004725 15120140443 0006073 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var css_what_1 = require("css-what"); var procedure_1 = require("./procedure"); var attributes = { exists: 10, equals: 8, not: 7, start: 6, end: 6, any: 5, hyphen: 4, element: 4, }; /** * Sort the parts of the passed selector, * as there is potential for optimization * (some types of selectors are faster than others) * * @param arr Selector to sort */ function sortByProcedure(arr) { var procs = arr.map(getProcedure); for (var i = 1; i < arr.length; i++) { var procNew = procs[i]; if (procNew < 0) continue; for (var j = i - 1; j >= 0 && procNew < procs[j]; j--) { var token = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = token; procs[j + 1] = procs[j]; procs[j] = procNew; } } } exports.default = sortByProcedure; function getProcedure(token) { var proc = procedure_1.procedure[token.type]; if (token.type === css_what_1.SelectorType.Attribute) { proc = attributes[token.action]; if (proc === attributes.equals && token.name === "id") { // Prefer ID selectors (eg. #ID) proc = 9; } if (token.ignoreCase) { /* * IgnoreCase adds some overhead, prefer "normal" token * this is a binary operation, to ensure it's still an int */ proc >>= 1; } } else if (token.type === css_what_1.SelectorType.Pseudo) { if (!token.data) { proc = 3; } else if (token.name === "has" || token.name === "contains") { proc = 0; // Expensive in any case } else if (Array.isArray(token.data)) { // "matches" and "not" proc = 0; for (var i = 0; i < token.data.length; i++) { // TODO better handling of complex selectors if (token.data[i].length !== 1) continue; var cur = getProcedure(token.data[i][0]); // Avoid executing :has or :contains if (cur === 0) { proc = 0; break; } if (cur > proc) proc = cur; } if (token.data.length > 1 && proc > 0) proc -= 1; } else { proc = 1; } } return proc; } types.d.ts 0000644 00000011322 15120140443 0006473 0 ustar 00 import type { Selector } from "css-what"; export declare type InternalSelector = Selector | { type: "_flexibleDescendant"; }; export declare type Predicate<Value> = (v: Value) => boolean; export interface Adapter<Node, ElementNode extends Node> { /** * Is the node a tag? */ isTag: (node: Node) => node is ElementNode; /** * Does at least one of passed element nodes pass the test predicate? */ existsOne: (test: Predicate<ElementNode>, elems: Node[]) => boolean; /** * Get the attribute value. */ getAttributeValue: (elem: ElementNode, name: string) => string | undefined; /** * Get the node's children */ getChildren: (node: Node) => Node[]; /** * Get the name of the tag */ getName: (elem: ElementNode) => string; /** * Get the parent of the node */ getParent: (node: ElementNode) => ElementNode | null; /** * Get the siblings of the node. Note that unlike jQuery's `siblings` method, * this is expected to include the current node as well */ getSiblings: (node: Node) => Node[]; /** * Returns the previous element sibling of a node. */ prevElementSibling?: (node: Node) => ElementNode | null; /** * Get the text content of the node, and its children if it has any. */ getText: (node: Node) => string; /** * Does the element have the named attribute? */ hasAttrib: (elem: ElementNode, name: string) => boolean; /** * Takes an array of nodes, and removes any duplicates, as well as any * nodes whose ancestors are also in the array. */ removeSubsets: (nodes: Node[]) => Node[]; /** * Finds all of the element nodes in the array that match the test predicate, * as well as any of their children that match it. */ findAll: (test: Predicate<ElementNode>, nodes: Node[]) => ElementNode[]; /** * Finds the first node in the array that matches the test predicate, or one * of its children. */ findOne: (test: Predicate<ElementNode>, elems: Node[]) => ElementNode | null; /** * The adapter can also optionally include an equals method, if your DOM * structure needs a custom equality test to compare two objects which refer * to the same underlying node. If not provided, `css-select` will fall back to * `a === b`. */ equals?: (a: Node, b: Node) => boolean; /** * Is the element in hovered state? */ isHovered?: (elem: ElementNode) => boolean; /** * Is the element in visited state? */ isVisited?: (elem: ElementNode) => boolean; /** * Is the element in active state? */ isActive?: (elem: ElementNode) => boolean; } export interface Options<Node, ElementNode extends Node> { /** * When enabled, tag names will be case-sensitive. * * @default false */ xmlMode?: boolean; /** * Lower-case attribute names. * * @default !xmlMode */ lowerCaseAttributeNames?: boolean; /** * Lower-case tag names. * * @default !xmlMode */ lowerCaseTags?: boolean; /** * Is the document in quirks mode? * * This will lead to .className and #id being case-insensitive. * * @default false */ quirksMode?: boolean; /** * The last function in the stack, will be called with the last element * that's looked at. */ rootFunc?: (element: ElementNode) => boolean; /** * The adapter to use when interacting with the backing DOM structure. By * default it uses the `domutils` module. */ adapter?: Adapter<Node, ElementNode>; /** * The context of the current query. Used to limit the scope of searches. * Can be matched directly using the `:scope` pseudo-selector. */ context?: Node | Node[]; /** * Allow css-select to cache results for some selectors, sometimes greatly * improving querying performance. Disable this if your document can * change in between queries with the same compiled selector. * * @default true */ cacheResults?: boolean; } export interface InternalOptions<Node, ElementNode extends Node> extends Options<Node, ElementNode> { adapter: Adapter<Node, ElementNode>; equals: (a: Node, b: Node) => boolean; } export interface CompiledQuery<ElementNode> { (node: ElementNode): boolean; shouldTestNextSiblings?: boolean; } export declare type Query<ElementNode> = string | CompiledQuery<ElementNode> | Selector[][]; export declare type CompileToken<Node, ElementNode extends Node> = (token: InternalSelector[][], options: InternalOptions<Node, ElementNode>, context?: Node[] | Node) => CompiledQuery<ElementNode>; //# sourceMappingURL=types.d.ts.map types.d.ts.map 0000644 00000005341 15120140443 0007253 0 ustar 00 {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,oBAAY,gBAAgB,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAE1E,oBAAY,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,KAAK,KAAK,OAAO,CAAC;AACrD,MAAM,WAAW,OAAO,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI;IACnD;;OAEG;IACH,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,IAAI,WAAW,CAAC;IAE3C;;OAEG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC;IAEpE;;OAEG;IACH,iBAAiB,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAE3E;;OAEG;IACH,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;IAEpC;;OAEG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,MAAM,CAAC;IAEvC;;OAEG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC;IAErD;;;OAGG;IACH,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;IAEpC;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,WAAW,GAAG,IAAI,CAAC;IAExD;;OAEG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IAEhC;;OAEG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAExD;;;OAGG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;IAEzC;;;OAGG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,CAAC;IAExE;;;OAGG;IACH,OAAO,EAAE,CACL,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,EAC5B,KAAK,EAAE,IAAI,EAAE,KACZ,WAAW,GAAG,IAAI,CAAC;IAExB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC;IAEvC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAE3C;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAE3C;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;CAC7C;AAED,MAAM,WAAW,OAAO,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI;IACnD;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC;IAC7C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC;IACxB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAGD,MAAM,WAAW,eAAe,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,CAC3D,SAAQ,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,aAAa,CAAC,WAAW;IACtC,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACpC;AACD,oBAAY,KAAK,CAAC,WAAW,IACvB,MAAM,GACN,aAAa,CAAC,WAAW,CAAC,GAC1B,QAAQ,EAAE,EAAE,CAAC;AACnB,oBAAY,YAAY,CAAC,IAAI,EAAE,WAAW,SAAS,IAAI,IAAI,CACvD,KAAK,EAAE,gBAAgB,EAAE,EAAE,EAC3B,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,EAC3C,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,KACtB,aAAa,CAAC,WAAW,CAAC,CAAC"} types.js 0000644 00000000115 15120140443 0006235 0 ustar 00 "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); parser.js 0000644 00000005600 15120140461 0006371 0 ustar 00 'use strict'; var TOKEN = /([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)/, NOTOKEN = /([^!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z])/g, QUOTED = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"/, PARAM = new RegExp(TOKEN.source + '(?:=(?:' + TOKEN.source + '|' + QUOTED.source + '))?'), EXT = new RegExp(TOKEN.source + '(?: *; *' + PARAM.source + ')*', 'g'), EXT_LIST = new RegExp('^' + EXT.source + '(?: *, *' + EXT.source + ')*$'), NUMBER = /^-?(0|[1-9][0-9]*)(\.[0-9]+)?$/; var hasOwnProperty = Object.prototype.hasOwnProperty; var Parser = { parseHeader: function(header) { var offers = new Offers(); if (header === '' || header === undefined) return offers; if (!EXT_LIST.test(header)) throw new SyntaxError('Invalid Sec-WebSocket-Extensions header: ' + header); var values = header.match(EXT); values.forEach(function(value) { var params = value.match(new RegExp(PARAM.source, 'g')), name = params.shift(), offer = {}; params.forEach(function(param) { var args = param.match(PARAM), key = args[1], data; if (args[2] !== undefined) { data = args[2]; } else if (args[3] !== undefined) { data = args[3].replace(/\\/g, ''); } else { data = true; } if (NUMBER.test(data)) data = parseFloat(data); if (hasOwnProperty.call(offer, key)) { offer[key] = [].concat(offer[key]); offer[key].push(data); } else { offer[key] = data; } }, this); offers.push(name, offer); }, this); return offers; }, serializeParams: function(name, params) { var values = []; var print = function(key, value) { if (value instanceof Array) { value.forEach(function(v) { print(key, v) }); } else if (value === true) { values.push(key); } else if (typeof value === 'number') { values.push(key + '=' + value); } else if (NOTOKEN.test(value)) { values.push(key + '="' + value.replace(/"/g, '\\"') + '"'); } else { values.push(key + '=' + value); } }; for (var key in params) print(key, params[key]); return [name].concat(values).join('; '); } }; var Offers = function() { this._byName = {}; this._inOrder = []; }; Offers.prototype.push = function(name, params) { if (!hasOwnProperty.call(this._byName, name)) this._byName[name] = []; this._byName[name].push(params); this._inOrder.push({ name: name, params: params }); }; Offers.prototype.eachOffer = function(callback, context) { var list = this._inOrder; for (var i = 0, n = list.length; i < n; i++) callback.call(context, list[i].name, list[i].params); }; Offers.prototype.byName = function(name) { return this._byName[name] || []; }; Offers.prototype.toArray = function() { return this._inOrder.slice(); }; module.exports = Parser; pipeline/README.md 0000644 00000060204 15120140461 0007624 0 ustar 00 # Extension pipelining `websocket-extensions` models the extension negotiation and processing pipeline of the WebSocket protocol. Between the driver parsing messages from the TCP stream and handing those messages off to the application, there may exist a stack of extensions that transform the message somehow. In the parlance of this framework, a *session* refers to a single instance of an extension, acting on a particular socket on either the server or the client side. A session may transform messages both incoming to the application and outgoing from the application, for example the `permessage-deflate` extension compresses outgoing messages and decompresses incoming messages. Message streams in either direction are independent; that is, incoming and outgoing messages cannot be assumed to 'pair up' as in a request-response protocol. Asynchronous processing of messages poses a number of problems that this pipeline construction is intended to solve. ## Overview Logically, we have the following: +-------------+ out +---+ +---+ +---+ +--------+ | |------>| |---->| |---->| |------>| | | Application | | A | | B | | C | | Driver | | |<------| |<----| |<----| |<------| | +-------------+ in +---+ +---+ +---+ +--------+ \ / +----------o----------+ | sessions For outgoing messages, the driver receives the result of C.outgoing(B.outgoing(A.outgoing(message))) or, [A, B, C].reduce(((m, ext) => ext.outgoing(m)), message) For incoming messages, the application receives the result of A.incoming(B.incoming(C.incoming(message))) or, [C, B, A].reduce(((m, ext) => ext.incoming(m)), message) A session is of the following type, to borrow notation from pseudo-Haskell: type Session = { incoming :: Message -> Message outgoing :: Message -> Message close :: () -> () } (That `() -> ()` syntax is intended to mean that `close()` is a nullary void method; I apologise to any Haskell readers for not using the right monad.) The `incoming()` and `outgoing()` methods perform message transformation in the respective directions; `close()` is called when a socket closes so the session can release any resources it's holding, for example a DEFLATE de/compression context. However because this is JavaScript, the `incoming()` and `outgoing()` methods may be asynchronous (indeed, `permessage-deflate` is based on `zlib`, whose API is stream-based). So their interface is strictly: type Session = { incoming :: Message -> Callback -> () outgoing :: Message -> Callback -> () close :: () -> () } type Callback = Either Error Message -> () This means a message *m2* can be pushed into a session while it's still processing the preceding message *m1*. The messages can be processed concurrently but they *must* be given to the next session in line (or to the application) in the same order they came in. Applications will expect to receive messages in the order they arrived over the wire, and sessions require this too. So ordering of messages must be preserved throughout the pipeline. Consider the following highly simplified extension that deflates messages on the wire. `message` is a value conforming the type: type Message = { rsv1 :: Boolean rsv2 :: Boolean rsv3 :: Boolean opcode :: Number data :: Buffer } Here's the extension: ```js var zlib = require('zlib'); var deflate = { outgoing: function(message, callback) { zlib.deflateRaw(message.data, function(error, result) { message.rsv1 = true; message.data = result; callback(error, message); }); }, incoming: function(message, callback) { // decompress inbound messages (elided) }, close: function() { // no state to clean up } }; ``` We can call it with a large message followed by a small one, and the small one will be returned first: ```js var crypto = require('crypto'), large = crypto.randomBytes(1 << 14), small = new Buffer('hi'); deflate.outgoing({ data: large }, function() { console.log(1, 'large'); }); deflate.outgoing({ data: small }, function() { console.log(2, 'small'); }); /* prints: 2 'small' 1 'large' */ ``` So a session that processes messages asynchronously may fail to preserve message ordering. Now, this extension is stateless, so it can process messages in any order and still produce the same output. But some extensions are stateful and require message order to be preserved. For example, when using `permessage-deflate` without `no_context_takeover` set, the session retains a DEFLATE de/compression context between messages, which accumulates state as it consumes data (later messages can refer to sections of previous ones to improve compression). Reordering parts of the DEFLATE stream will result in a failed decompression. Messages must be decompressed in the same order they were compressed by the peer in order for the DEFLATE protocol to work. Finally, there is the problem of closing a socket. When a WebSocket is closed by the application, or receives a closing request from the other peer, there may be messages outgoing from the application and incoming from the peer in the pipeline. If we close the socket and pipeline immediately, two problems arise: * We may send our own closing frame to the peer before all prior messages we sent have been written to the socket, and before we have finished processing all prior messages from the peer * The session may be instructed to close its resources (e.g. its de/compression context) while it's in the middle of processing a message, or before it has received messages that are upstream of it in the pipeline Essentially, we must defer closing the sessions and sending a closing frame until after all prior messages have exited the pipeline. ## Design goals * Message order must be preserved between the protocol driver, the extension sessions, and the application * Messages should be handed off to sessions and endpoints as soon as possible, to maximise throughput of stateless sessions * The closing procedure should block any further messages from entering the pipeline, and should allow all existing messages to drain * Sessions should be closed as soon as possible to prevent them holding memory and other resources when they have no more messages to handle * The closing API should allow the caller to detect when the pipeline is empty and it is safe to continue the WebSocket closing procedure * Individual extensions should remain as simple as possible to facilitate modularity and independent authorship The final point about modularity is an important one: this framework is designed to facilitate extensions existing as plugins, by decoupling the protocol driver, extensions, and application. In an ideal world, plugins should only need to contain code for their specific functionality, and not solve these problems that apply to all sessions. Also, solving some of these problems requires consideration of all active sessions collectively, which an individual session is incapable of doing. For example, it is entirely possible to take the simple `deflate` extension above and wrap its `incoming()` and `outgoing()` methods in two `Transform` streams, producing this type: type Session = { incoming :: TransformStream outtoing :: TransformStream close :: () -> () } The `Transform` class makes it easy to wrap an async function such that message order is preserved: ```js var stream = require('stream'), session = new stream.Transform({ objectMode: true }); session._transform = function(message, _, callback) { var self = this; deflate.outgoing(message, function(error, result) { self.push(result); callback(); }); }; ``` However, this has a negative impact on throughput: it works by deferring `callback()` until the async function has 'returned', which blocks `Transform` from passing further input into the `_transform()` method until the current message is dealt with completely. This would prevent sessions from processing messages concurrently, and would unnecessarily reduce the throughput of stateless extensions. So, input should be handed off to sessions as soon as possible, and all we need is a mechanism to reorder the output so that message order is preserved for the next session in line. ## Solution We now describe the model implemented here and how it meets the above design goals. The above diagram where a stack of extensions sit between the driver and application describes the data flow, but not the object graph. That looks like this: +--------+ | Driver | +---o----+ | V +------------+ +----------+ | Extensions o----->| Pipeline | +------------+ +-----o----+ | +---------------+---------------+ | | | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ A driver using this framework holds an instance of the `Extensions` class, which it uses to register extension plugins, negotiate headers and transform messages. The `Extensions` instance itself holds a `Pipeline`, which contains an array of `Cell` objects, each of which wraps one of the sessions. ### Message processing Both the `Pipeline` and `Cell` classes have `incoming()` and `outgoing()` methods; the `Pipeline` interface pushes messages into the pipe, delegates the message to each `Cell` in turn, then returns it back to the driver. Outgoing messages pass through `A` then `B` then `C`, and incoming messages in the reverse order. Internally, a `Cell` contains two `Functor` objects. A `Functor` wraps an async function and makes sure its output messages maintain the order of its input messages. This name is due to [@fronx](https://github.com/fronx), on the basis that, by preserving message order, the abstraction preserves the *mapping* between input and output messages. To use our simple `deflate` extension from above: ```js var functor = new Functor(deflate, 'outgoing'); functor.call({ data: large }, function() { console.log(1, 'large'); }); functor.call({ data: small }, function() { console.log(2, 'small'); }); /* -> 1 'large' 2 'small' */ ``` A `Cell` contains two of these, one for each direction: +-----------------------+ +---->| Functor [A, incoming] | +----------+ | +-----------------------+ | Cell [A] o------+ +----------+ | +-----------------------+ +---->| Functor [A, outgoing] | +-----------------------+ This satisfies the message transformation requirements: the `Pipeline` simply loops over the cells in the appropriate direction to transform each message. Because each `Cell` will preserve message order, we can pass a message to the next `Cell` in line as soon as the current `Cell` returns it. This gives each `Cell` all the messages in order while maximising throughput. ### Session closing We want to close each session as soon as possible, after all existing messages have drained. To do this, each `Cell` begins with a pending message counter in each direction, labelled `in` and `out` below. +----------+ | Pipeline | +-----o----+ | +---------------+---------------+ | | | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 0 out: 0 out: 0 When a message *m1* enters the pipeline, say in the `outgoing` direction, we increment the `pending.out` counter on all cells immediately. +----------+ m1 => | Pipeline | +-----o----+ | +---------------+---------------+ | | | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 1 out: 1 out: 1 *m1* is handed off to `A`, meanwhile a second message `m2` arrives in the same direction. All `pending.out` counters are again incremented. +----------+ m2 => | Pipeline | +-----o----+ | +---------------+---------------+ m1 | | | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 2 out: 2 out: 2 When the first cell's `A.outgoing` functor finishes processing *m1*, the first `pending.out` counter is decremented and *m1* is handed off to cell `B`. +----------+ | Pipeline | +-----o----+ | +---------------+---------------+ m2 | m1 | | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 1 out: 2 out: 2 As `B` finishes with *m1*, and as `A` finishes with *m2*, the `pending.out` counters continue to decrement. +----------+ | Pipeline | +-----o----+ | +---------------+---------------+ | m2 | m1 | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 0 out: 1 out: 2 Say `C` is a little slow, and begins processing *m2* while still processing *m1*. That's fine, the `Functor` mechanism will keep *m1* ahead of *m2* in the output. +----------+ | Pipeline | +-----o----+ | +---------------+---------------+ | | m2 | m1 +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 0 out: 0 out: 2 Once all messages are dealt with, the counters return to `0`. +----------+ | Pipeline | +-----o----+ | +---------------+---------------+ | | | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 0 out: 0 out: 0 The same process applies in the `incoming` direction, the only difference being that messages are passed to `C` first. This makes closing the sessions quite simple. When the driver wants to close the socket, it calls `Pipeline.close()`. This *immediately* calls `close()` on all the cells. If a cell has `in == out == 0`, then it immediately calls `session.close()`. Otherwise, it stores the closing call and defers it until `in` and `out` have both ticked down to zero. The pipeline will not accept new messages after `close()` has been called, so we know the pending counts will not increase after this point. This means each session is closed as soon as possible: `A` can close while the slow `C` session is still working, because it knows there are no more messages on the way. Similarly, `C` will defer closing if `close()` is called while *m1* is still in `B`, and *m2* in `A`, because its pending count means it knows it has work yet to do, even if it's not received those messages yet. This concern cannot be addressed by extensions acting only on their own local state, unless we pollute individual extensions by making them all implement this same mechanism. The actual closing API at each level is slightly different: type Session = { close :: () -> () } type Cell = { close :: () -> Promise () } type Pipeline = { close :: Callback -> () } This might appear inconsistent so it's worth explaining. Remember that a `Pipeline` holds a list of `Cell` objects, each wrapping a `Session`. The driver talks (via the `Extensions` API) to the `Pipeline` interface, and it wants `Pipeline.close()` to do two things: close all the sessions, and tell me when it's safe to start the closing procedure (i.e. when all messages have drained from the pipe and been handed off to the application or socket). A callback API works well for that. At the other end of the stack, `Session.close()` is a nullary void method with no callback or promise API because we don't care what it does, and whatever it does do will not block the WebSocket protocol; we're not going to hold off processing messages while a session closes its de/compression context. We just tell it to close itself, and don't want to wait while it does that. In the middle, `Cell.close()` returns a promise rather than using a callback. This is for two reasons. First, `Cell.close()` might not do anything immediately, it might have to defer its effect while messages drain. So, if given a callback, it would have to store it in a queue for later execution. Callbacks work fine if your method does something and can then invoke the callback itself, but if you need to store callbacks somewhere so another method can execute them, a promise is a better fit. Second, it better serves the purposes of `Pipeline.close()`: it wants to call `close()` on each of a list of cells, and wait for all of them to finish. This is simple and idiomatic using promises: ```js var closed = cells.map((cell) => cell.close()); Promise.all(closed).then(callback); ``` (We don't actually use a full *Promises/A+* compatible promise here, we use a much simplified construction that acts as a callback aggregater and resolves synchronously and does not support chaining, but the principle is the same.) ### Error handling We've not mentioned error handling so far but it bears some explanation. The above counter system still applies, but behaves slightly differently in the presence of errors. Say we push three messages into the pipe in the outgoing direction: +----------+ m3, m2, m1 => | Pipeline | +-----o----+ | +---------------+---------------+ | | | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 3 out: 3 out: 3 They pass through the cells successfully up to this point: +----------+ | Pipeline | +-----o----+ | +---------------+---------------+ m3 | m2 | m1 | +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 1 out: 2 out: 3 At this point, session `B` produces an error while processing *m2*, that is *m2* becomes *e2*. *m1* is still in the pipeline, and *m3* is queued behind *m2*. What ought to happen is that *m1* is handed off to the socket, then *m2* is released to the driver, which will detect the error and begin closing the socket. No further processing should be done on *m3* and it should not be released to the driver after the error is emitted. To handle this, we allow errors to pass down the pipeline just like messages do, to maintain ordering. But, once a cell sees its session produce an error, or it receives an error from upstream, it should refuse to accept any further messages. Session `B` might have begun processing *m3* by the time it produces the error *e2*, but `C` will have been given *e2* before it receives *m3*, and can simply drop *m3*. Now, say *e2* reaches the slow session `C` while *m1* is still present, meanwhile *m3* has been dropped. `C` will never receive *m3* since it will have been dropped upstream. Under the present model, its `out` counter will be `3` but it is only going to emit two more values: *m1* and *e2*. In order for closing to work, we need to decrement `out` to reflect this. The situation should look like this: +----------+ | Pipeline | +-----o----+ | +---------------+---------------+ | | e2 | m1 +-----o----+ +-----o----+ +-----o----+ | Cell [A] | | Cell [B] | | Cell [C] | +----------+ +----------+ +----------+ in: 0 in: 0 in: 0 out: 0 out: 0 out: 2 When a cell sees its session emit an error, or when it receives an error from upstream, it sets its pending count in the appropriate direction to equal the number of messages it is *currently* processing. It will not accept any messages after it sees the error, so this will allow the counter to reach zero. Note that while *e2* is in the pipeline, `Pipeline` should drop any further messages in the outgoing direction, but should continue to accept incoming messages. Until *e2* makes it out of the pipe to the driver, behind previous successful messages, the driver does not know an error has happened, and a message may arrive over the socket and make it all the way through the incoming pipe in the meantime. We only halt processing in the affected direction to avoid doing unnecessary work since messages arriving after an error should not be processed. Some unnecessary work may happen, for example any messages already in the pipeline following *m2* will be processed by `A`, since it's upstream of the error. Those messages will be dropped by `B`. ## Alternative ideas I am considering implementing `Functor` as an object-mode transform stream rather than what is essentially an async function. Being object-mode, a stream would preserve message boundaries and would also possibly help address back-pressure. I'm not sure whether this would require external API changes so that such streams could be connected to the downstream driver's streams. ## Acknowledgements Credit is due to [@mnowster](https://github.com/mnowster) for helping with the design and to [@fronx](https://github.com/fronx) for helping name things. pipeline/cell.js 0000644 00000002676 15120140461 0007633 0 ustar 00 'use strict'; var Functor = require('./functor'), Pledge = require('./pledge'); var Cell = function(tuple) { this._ext = tuple[0]; this._session = tuple[1]; this._functors = { incoming: new Functor(this._session, 'processIncomingMessage'), outgoing: new Functor(this._session, 'processOutgoingMessage') }; }; Cell.prototype.pending = function(direction) { var functor = this._functors[direction]; if (!functor._stopped) functor.pending += 1; }; Cell.prototype.incoming = function(error, message, callback, context) { this._exec('incoming', error, message, callback, context); }; Cell.prototype.outgoing = function(error, message, callback, context) { this._exec('outgoing', error, message, callback, context); }; Cell.prototype.close = function() { this._closed = this._closed || new Pledge(); this._doClose(); return this._closed; }; Cell.prototype._exec = function(direction, error, message, callback, context) { this._functors[direction].call(error, message, function(err, msg) { if (err) err.message = this._ext.name + ': ' + err.message; callback.call(context, err, msg); this._doClose(); }, this); }; Cell.prototype._doClose = function() { var fin = this._functors.incoming, fout = this._functors.outgoing; if (!this._closed || fin.pending + fout.pending !== 0) return; if (this._session) this._session.close(); this._session = null; this._closed.done(); }; module.exports = Cell; pipeline/functor.js 0000644 00000002762 15120140461 0010370 0 ustar 00 'use strict'; var RingBuffer = require('./ring_buffer'); var Functor = function(session, method) { this._session = session; this._method = method; this._queue = new RingBuffer(Functor.QUEUE_SIZE); this._stopped = false; this.pending = 0; }; Functor.QUEUE_SIZE = 8; Functor.prototype.call = function(error, message, callback, context) { if (this._stopped) return; var record = { error: error, message: message, callback: callback, context: context, done: false }, called = false, self = this; this._queue.push(record); if (record.error) { record.done = true; this._stop(); return this._flushQueue(); } var handler = function(err, msg) { if (!(called ^ (called = true))) return; if (err) { self._stop(); record.error = err; record.message = null; } else { record.message = msg; } record.done = true; self._flushQueue(); }; try { this._session[this._method](message, handler); } catch (err) { handler(err); } }; Functor.prototype._stop = function() { this.pending = this._queue.length; this._stopped = true; }; Functor.prototype._flushQueue = function() { var queue = this._queue, record; while (queue.length > 0 && queue.peek().done) { record = queue.shift(); if (record.error) { this.pending = 0; queue.clear(); } else { this.pending -= 1; } record.callback.call(record.context, record.error, record.message); } }; module.exports = Functor; pipeline/index.js 0000644 00000002710 15120140461 0010010 0 ustar 00 'use strict'; var Cell = require('./cell'), Pledge = require('./pledge'); var Pipeline = function(sessions) { this._cells = sessions.map(function(session) { return new Cell(session) }); this._stopped = { incoming: false, outgoing: false }; }; Pipeline.prototype.processIncomingMessage = function(message, callback, context) { if (this._stopped.incoming) return; this._loop('incoming', this._cells.length - 1, -1, -1, message, callback, context); }; Pipeline.prototype.processOutgoingMessage = function(message, callback, context) { if (this._stopped.outgoing) return; this._loop('outgoing', 0, this._cells.length, 1, message, callback, context); }; Pipeline.prototype.close = function(callback, context) { this._stopped = { incoming: true, outgoing: true }; var closed = this._cells.map(function(a) { return a.close() }); if (callback) Pledge.all(closed).then(function() { callback.call(context) }); }; Pipeline.prototype._loop = function(direction, start, end, step, message, callback, context) { var cells = this._cells, n = cells.length, self = this; while (n--) cells[n].pending(direction); var pipe = function(index, error, msg) { if (index === end) return callback.call(context, error, msg); cells[index][direction](error, msg, function(err, m) { if (err) self._stopped[direction] = true; pipe(index + step, err, m); }); }; pipe(start, null, message); }; module.exports = Pipeline; pipeline/pledge.js 0000644 00000001413 15120140461 0010140 0 ustar 00 'use strict'; var RingBuffer = require('./ring_buffer'); var Pledge = function() { this._complete = false; this._callbacks = new RingBuffer(Pledge.QUEUE_SIZE); }; Pledge.QUEUE_SIZE = 4; Pledge.all = function(list) { var pledge = new Pledge(), pending = list.length, n = pending; if (pending === 0) pledge.done(); while (n--) list[n].then(function() { pending -= 1; if (pending === 0) pledge.done(); }); return pledge; }; Pledge.prototype.then = function(callback) { if (this._complete) callback(); else this._callbacks.push(callback); }; Pledge.prototype.done = function() { this._complete = true; var callbacks = this._callbacks, callback; while (callback = callbacks.shift()) callback(); }; module.exports = Pledge; pipeline/ring_buffer.js 0000644 00000003207 15120140461 0011173 0 ustar 00 'use strict'; var RingBuffer = function(bufferSize) { this._bufferSize = bufferSize; this.clear(); }; RingBuffer.prototype.clear = function() { this._buffer = new Array(this._bufferSize); this._ringOffset = 0; this._ringSize = this._bufferSize; this._head = 0; this._tail = 0; this.length = 0; }; RingBuffer.prototype.push = function(value) { var expandBuffer = false, expandRing = false; if (this._ringSize < this._bufferSize) { expandBuffer = (this._tail === 0); } else if (this._ringOffset === this._ringSize) { expandBuffer = true; expandRing = (this._tail === 0); } if (expandBuffer) { this._tail = this._bufferSize; this._buffer = this._buffer.concat(new Array(this._bufferSize)); this._bufferSize = this._buffer.length; if (expandRing) this._ringSize = this._bufferSize; } this._buffer[this._tail] = value; this.length += 1; if (this._tail < this._ringSize) this._ringOffset += 1; this._tail = (this._tail + 1) % this._bufferSize; }; RingBuffer.prototype.peek = function() { if (this.length === 0) return void 0; return this._buffer[this._head]; }; RingBuffer.prototype.shift = function() { if (this.length === 0) return void 0; var value = this._buffer[this._head]; this._buffer[this._head] = void 0; this.length -= 1; this._ringOffset -= 1; if (this._ringOffset === 0 && this.length > 0) { this._head = this._ringSize; this._ringOffset = this.length; this._ringSize = this._bufferSize; } else { this._head = (this._head + 1) % this._ringSize; } return value; }; module.exports = RingBuffer; websocket_extensions.js 0000644 00000011401 15120140461 0011336 0 ustar 00 'use strict'; var Parser = require('./parser'), Pipeline = require('./pipeline'); var Extensions = function() { this._rsv1 = this._rsv2 = this._rsv3 = null; this._byName = {}; this._inOrder = []; this._sessions = []; this._index = {}; }; Extensions.MESSAGE_OPCODES = [1, 2]; var instance = { add: function(ext) { if (typeof ext.name !== 'string') throw new TypeError('extension.name must be a string'); if (ext.type !== 'permessage') throw new TypeError('extension.type must be "permessage"'); if (typeof ext.rsv1 !== 'boolean') throw new TypeError('extension.rsv1 must be true or false'); if (typeof ext.rsv2 !== 'boolean') throw new TypeError('extension.rsv2 must be true or false'); if (typeof ext.rsv3 !== 'boolean') throw new TypeError('extension.rsv3 must be true or false'); if (this._byName.hasOwnProperty(ext.name)) throw new TypeError('An extension with name "' + ext.name + '" is already registered'); this._byName[ext.name] = ext; this._inOrder.push(ext); }, generateOffer: function() { var sessions = [], offer = [], index = {}; this._inOrder.forEach(function(ext) { var session = ext.createClientSession(); if (!session) return; var record = [ext, session]; sessions.push(record); index[ext.name] = record; var offers = session.generateOffer(); offers = offers ? [].concat(offers) : []; offers.forEach(function(off) { offer.push(Parser.serializeParams(ext.name, off)); }, this); }, this); this._sessions = sessions; this._index = index; return offer.length > 0 ? offer.join(', ') : null; }, activate: function(header) { var responses = Parser.parseHeader(header), sessions = []; responses.eachOffer(function(name, params) { var record = this._index[name]; if (!record) throw new Error('Server sent an extension response for unknown extension "' + name + '"'); var ext = record[0], session = record[1], reserved = this._reserved(ext); if (reserved) throw new Error('Server sent two extension responses that use the RSV' + reserved[0] + ' bit: "' + reserved[1] + '" and "' + ext.name + '"'); if (session.activate(params) !== true) throw new Error('Server sent unacceptable extension parameters: ' + Parser.serializeParams(name, params)); this._reserve(ext); sessions.push(record); }, this); this._sessions = sessions; this._pipeline = new Pipeline(sessions); }, generateResponse: function(header) { var sessions = [], response = [], offers = Parser.parseHeader(header); this._inOrder.forEach(function(ext) { var offer = offers.byName(ext.name); if (offer.length === 0 || this._reserved(ext)) return; var session = ext.createServerSession(offer); if (!session) return; this._reserve(ext); sessions.push([ext, session]); response.push(Parser.serializeParams(ext.name, session.generateResponse())); }, this); this._sessions = sessions; this._pipeline = new Pipeline(sessions); return response.length > 0 ? response.join(', ') : null; }, validFrameRsv: function(frame) { var allowed = { rsv1: false, rsv2: false, rsv3: false }, ext; if (Extensions.MESSAGE_OPCODES.indexOf(frame.opcode) >= 0) { for (var i = 0, n = this._sessions.length; i < n; i++) { ext = this._sessions[i][0]; allowed.rsv1 = allowed.rsv1 || ext.rsv1; allowed.rsv2 = allowed.rsv2 || ext.rsv2; allowed.rsv3 = allowed.rsv3 || ext.rsv3; } } return (allowed.rsv1 || !frame.rsv1) && (allowed.rsv2 || !frame.rsv2) && (allowed.rsv3 || !frame.rsv3); }, processIncomingMessage: function(message, callback, context) { this._pipeline.processIncomingMessage(message, callback, context); }, processOutgoingMessage: function(message, callback, context) { this._pipeline.processOutgoingMessage(message, callback, context); }, close: function(callback, context) { if (!this._pipeline) return callback.call(context); this._pipeline.close(callback, context); }, _reserve: function(ext) { this._rsv1 = this._rsv1 || (ext.rsv1 && ext.name); this._rsv2 = this._rsv2 || (ext.rsv2 && ext.name); this._rsv3 = this._rsv3 || (ext.rsv3 && ext.name); }, _reserved: function(ext) { if (this._rsv1 && ext.rsv1) return [1, this._rsv1]; if (this._rsv2 && ext.rsv2) return [2, this._rsv2]; if (this._rsv3 && ext.rsv3) return [3, this._rsv3]; return false; } }; for (var key in instance) Extensions.prototype[key] = instance[key]; module.exports = Extensions; foreach.js 0000644 00000004167 15120141140 0006506 0 ustar 00 /*! * Sync/Async forEach * https://github.com/cowboy/javascript-sync-async-foreach * * Copyright (c) 2012 "Cowboy" Ben Alman * Licensed under the MIT license. * http://benalman.com/about/license/ */ (function(exports) { // Iterate synchronously or asynchronously. exports.forEach = function(arr, eachFn, doneFn) { var i = -1; // Resolve array length to a valid (ToUint32) number. var len = arr.length >>> 0; // This IIFE is called once now, and then again, by name, for each loop // iteration. (function next(result) { // This flag will be set to true if `this.async` is called inside the // eachFn` callback. var async; // Was false returned from the `eachFn` callback or passed to the // `this.async` done function? var abort = result === false; // Increment counter variable and skip any indices that don't exist. This // allows sparse arrays to be iterated. do { ++i; } while (!(i in arr) && i !== len); // Exit if result passed to `this.async` done function or returned from // the `eachFn` callback was false, or when done iterating. if (abort || i === len) { // If a `doneFn` callback was specified, invoke that now. Pass in a // boolean value representing "not aborted" state along with the array. if (doneFn) { doneFn(!abort, arr); } return; } // Invoke the `eachFn` callback, setting `this` inside the callback to a // custom object that contains one method, and passing in the array item, // index, and the array. result = eachFn.call({ // If `this.async` is called inside the `eachFn` callback, set the async // flag and return a function that can be used to continue iterating. async: function() { async = true; return next; } }, arr[i], i, arr); // If the async flag wasn't set, continue by calling `next` synchronously, // passing in the result of the `eachFn` callback. if (!async) { next(result); } }()); }; }(typeof exports === "object" && exports || this)); util.js 0000644 00000005737 15120223044 0006064 0 ustar 00 // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // NOTE: These type checking functions intentionally don't use `instanceof` // because it is fragile and can be easily faked with `Object.create()`. function isArray(arg) { if (Array.isArray) { return Array.isArray(arg); } return objectToString(arg) === '[object Array]'; } exports.isArray = isArray; function isBoolean(arg) { return typeof arg === 'boolean'; } exports.isBoolean = isBoolean; function isNull(arg) { return arg === null; } exports.isNull = isNull; function isNullOrUndefined(arg) { return arg == null; } exports.isNullOrUndefined = isNullOrUndefined; function isNumber(arg) { return typeof arg === 'number'; } exports.isNumber = isNumber; function isString(arg) { return typeof arg === 'string'; } exports.isString = isString; function isSymbol(arg) { return typeof arg === 'symbol'; } exports.isSymbol = isSymbol; function isUndefined(arg) { return arg === void 0; } exports.isUndefined = isUndefined; function isRegExp(re) { return objectToString(re) === '[object RegExp]'; } exports.isRegExp = isRegExp; function isObject(arg) { return typeof arg === 'object' && arg !== null; } exports.isObject = isObject; function isDate(d) { return objectToString(d) === '[object Date]'; } exports.isDate = isDate; function isError(e) { return (objectToString(e) === '[object Error]' || e instanceof Error); } exports.isError = isError; function isFunction(arg) { return typeof arg === 'function'; } exports.isFunction = isFunction; function isPrimitive(arg) { return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || typeof arg === 'symbol' || // ES6 symbol typeof arg === 'undefined'; } exports.isPrimitive = isPrimitive; exports.isBuffer = require('buffer').Buffer.isBuffer; function objectToString(o) { return Object.prototype.toString.call(o); } Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php 0000644 00000020661 15120240431 0020521 0 ustar 00 <?php namespace Doctrine\Common\Collections\Expr; use ArrayAccess; use Closure; use RuntimeException; use function explode; use function in_array; use function is_array; use function is_scalar; use function iterator_to_array; use function method_exists; use function preg_match; use function preg_replace_callback; use function strlen; use function strpos; use function strtoupper; use function substr; /** * Walks an expression graph and turns it into a PHP closure. * * This closure can be used with {@Collection#filter()} and is used internally * by {@ArrayCollection#select()}. */ class ClosureExpressionVisitor extends ExpressionVisitor { /** * Accesses the field of a given object. This field has to be public * directly or indirectly (through an accessor get*, is*, or a magic * method, __get, __call). * * @param object|mixed[] $object * @param string $field * * @return mixed */ public static function getObjectFieldValue($object, $field) { if (strpos($field, '.') !== false) { [$field, $subField] = explode('.', $field, 2); $object = self::getObjectFieldValue($object, $field); return self::getObjectFieldValue($object, $subField); } if (is_array($object)) { return $object[$field]; } $accessors = ['get', 'is', '']; foreach ($accessors as $accessor) { $accessor .= $field; if (method_exists($object, $accessor)) { return $object->$accessor(); } } if (preg_match('/^is[A-Z]+/', $field) === 1 && method_exists($object, $field)) { return $object->$field(); } // __call should be triggered for get. $accessor = $accessors[0] . $field; if (method_exists($object, '__call')) { return $object->$accessor(); } if ($object instanceof ArrayAccess) { return $object[$field]; } if (isset($object->$field)) { return $object->$field; } // camelcase field name to support different variable naming conventions $ccField = preg_replace_callback('/_(.?)/', static function ($matches) { return strtoupper($matches[1]); }, $field); foreach ($accessors as $accessor) { $accessor .= $ccField; if (method_exists($object, $accessor)) { return $object->$accessor(); } } return $object->$field; } /** * Helper for sorting arrays of objects based on multiple fields + orientations. * * @param string $name * @param int $orientation * * @return Closure */ public static function sortByField($name, $orientation = 1, ?Closure $next = null) { if (! $next) { $next = static function (): int { return 0; }; } return static function ($a, $b) use ($name, $next, $orientation): int { $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); if ($aValue === $bValue) { return $next($a, $b); } return ($aValue > $bValue ? 1 : -1) * $orientation; }; } /** * {@inheritDoc} */ public function walkComparison(Comparison $comparison) { $field = $comparison->getField(); $value = $comparison->getValue()->getValue(); // shortcut for walkValue() switch ($comparison->getOperator()) { case Comparison::EQ: return static function ($object) use ($field, $value): bool { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; }; case Comparison::NEQ: return static function ($object) use ($field, $value): bool { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; }; case Comparison::LT: return static function ($object) use ($field, $value): bool { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; }; case Comparison::LTE: return static function ($object) use ($field, $value): bool { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; }; case Comparison::GT: return static function ($object) use ($field, $value): bool { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; }; case Comparison::GTE: return static function ($object) use ($field, $value): bool { return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; }; case Comparison::IN: return static function ($object) use ($field, $value): bool { $fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field); return in_array($fieldValue, $value, is_scalar($fieldValue)); }; case Comparison::NIN: return static function ($object) use ($field, $value): bool { $fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field); return ! in_array($fieldValue, $value, is_scalar($fieldValue)); }; case Comparison::CONTAINS: return static function ($object) use ($field, $value) { return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) !== false; }; case Comparison::MEMBER_OF: return static function ($object) use ($field, $value): bool { $fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field); if (! is_array($fieldValues)) { $fieldValues = iterator_to_array($fieldValues); } return in_array($value, $fieldValues, true); }; case Comparison::STARTS_WITH: return static function ($object) use ($field, $value): bool { return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) === 0; }; case Comparison::ENDS_WITH: return static function ($object) use ($field, $value): bool { return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($value)); }; default: throw new RuntimeException('Unknown comparison operator: ' . $comparison->getOperator()); } } /** * {@inheritDoc} */ public function walkValue(Value $value) { return $value->getValue(); } /** * {@inheritDoc} */ public function walkCompositeExpression(CompositeExpression $expr) { $expressionList = []; foreach ($expr->getExpressionList() as $child) { $expressionList[] = $this->dispatch($child); } switch ($expr->getType()) { case CompositeExpression::TYPE_AND: return $this->andExpressions($expressionList); case CompositeExpression::TYPE_OR: return $this->orExpressions($expressionList); default: throw new RuntimeException('Unknown composite ' . $expr->getType()); } } /** @param callable[] $expressions */ private function andExpressions(array $expressions): callable { return static function ($object) use ($expressions): bool { foreach ($expressions as $expression) { if (! $expression($object)) { return false; } } return true; }; } /** @param callable[] $expressions */ private function orExpressions(array $expressions): callable { return static function ($object) use ($expressions): bool { foreach ($expressions as $expression) { if ($expression($object)) { return true; } } return false; }; } } Doctrine/Common/Collections/Expr/Comparison.php 0000644 00000003145 15120240431 0015555 0 ustar 00 <?php namespace Doctrine\Common\Collections\Expr; /** * Comparison of a field with a value by the given operator. */ class Comparison implements Expression { public const EQ = '='; public const NEQ = '<>'; public const LT = '<'; public const LTE = '<='; public const GT = '>'; public const GTE = '>='; public const IS = '='; // no difference with EQ public const IN = 'IN'; public const NIN = 'NIN'; public const CONTAINS = 'CONTAINS'; public const MEMBER_OF = 'MEMBER_OF'; public const STARTS_WITH = 'STARTS_WITH'; public const ENDS_WITH = 'ENDS_WITH'; /** @var string */ private $field; /** @var string */ private $op; /** @var Value */ private $value; /** * @param string $field * @param string $operator * @param mixed $value */ public function __construct($field, $operator, $value) { if (! ($value instanceof Value)) { $value = new Value($value); } $this->field = $field; $this->op = $operator; $this->value = $value; } /** @return string */ public function getField() { return $this->field; } /** @return Value */ public function getValue() { return $this->value; } /** @return string */ public function getOperator() { return $this->op; } /** * {@inheritDoc} */ public function visit(ExpressionVisitor $visitor) { return $visitor->walkComparison($this); } } Doctrine/Common/Collections/Expr/CompositeExpression.php 0000644 00000002707 15120240431 0017470 0 ustar 00 <?php namespace Doctrine\Common\Collections\Expr; use RuntimeException; /** * Expression of Expressions combined by AND or OR operation. */ class CompositeExpression implements Expression { public const TYPE_AND = 'AND'; public const TYPE_OR = 'OR'; /** @var string */ private $type; /** @var Expression[] */ private $expressions = []; /** * @param string $type * @param mixed[] $expressions * * @throws RuntimeException */ public function __construct($type, array $expressions) { $this->type = $type; foreach ($expressions as $expr) { if ($expr instanceof Value) { throw new RuntimeException('Values are not supported expressions as children of and/or expressions.'); } if (! ($expr instanceof Expression)) { throw new RuntimeException('No expression given to CompositeExpression.'); } $this->expressions[] = $expr; } } /** * Returns the list of expressions nested in this composite. * * @return Expression[] */ public function getExpressionList() { return $this->expressions; } /** @return string */ public function getType() { return $this->type; } /** * {@inheritDoc} */ public function visit(ExpressionVisitor $visitor) { return $visitor->walkCompositeExpression($this); } } Doctrine/Common/Collections/Expr/Expression.php 0000644 00000000331 15120240431 0015574 0 ustar 00 <?php namespace Doctrine\Common\Collections\Expr; /** * Expression for the {@link Selectable} interface. */ interface Expression { /** @return mixed */ public function visit(ExpressionVisitor $visitor); } Doctrine/Common/Collections/Expr/ExpressionVisitor.php 0000644 00000002724 15120240431 0017164 0 ustar 00 <?php namespace Doctrine\Common\Collections\Expr; use RuntimeException; use function get_class; /** * An Expression visitor walks a graph of expressions and turns them into a * query for the underlying implementation. */ abstract class ExpressionVisitor { /** * Converts a comparison expression into the target query language output. * * @return mixed */ abstract public function walkComparison(Comparison $comparison); /** * Converts a value expression into the target query language part. * * @return mixed */ abstract public function walkValue(Value $value); /** * Converts a composite expression into the target query language output. * * @return mixed */ abstract public function walkCompositeExpression(CompositeExpression $expr); /** * Dispatches walking an expression to the appropriate handler. * * @return mixed * * @throws RuntimeException */ public function dispatch(Expression $expr) { switch (true) { case $expr instanceof Comparison: return $this->walkComparison($expr); case $expr instanceof Value: return $this->walkValue($expr); case $expr instanceof CompositeExpression: return $this->walkCompositeExpression($expr); default: throw new RuntimeException('Unknown Expression ' . get_class($expr)); } } } Doctrine/Common/Collections/Expr/Value.php 0000644 00000000754 15120240431 0014522 0 ustar 00 <?php namespace Doctrine\Common\Collections\Expr; class Value implements Expression { /** @var mixed */ private $value; /** @param mixed $value */ public function __construct($value) { $this->value = $value; } /** @return mixed */ public function getValue() { return $this->value; } /** * {@inheritDoc} */ public function visit(ExpressionVisitor $visitor) { return $visitor->walkValue($this); } } Doctrine/Common/Collections/AbstractLazyCollection.php 0000644 00000015337 15120240431 0017152 0 ustar 00 <?php namespace Doctrine\Common\Collections; use Closure; use LogicException; use ReturnTypeWillChange; use Traversable; /** * Lazy collection that is backed by a concrete collection * * @psalm-template TKey of array-key * @psalm-template T * @template-implements Collection<TKey,T> */ abstract class AbstractLazyCollection implements Collection { /** * The backed collection to use * * @psalm-var Collection<TKey,T>|null * @var Collection<mixed>|null */ protected $collection; /** @var bool */ protected $initialized = false; /** * {@inheritDoc} * * @return int */ #[ReturnTypeWillChange] public function count() { $this->initialize(); return $this->collection->count(); } /** * {@inheritDoc} */ public function add($element) { $this->initialize(); return $this->collection->add($element); } /** * {@inheritDoc} */ public function clear() { $this->initialize(); $this->collection->clear(); } /** * {@inheritDoc} * * @template TMaybeContained */ public function contains($element) { $this->initialize(); return $this->collection->contains($element); } /** * {@inheritDoc} */ public function isEmpty() { $this->initialize(); return $this->collection->isEmpty(); } /** * {@inheritDoc} */ public function remove($key) { $this->initialize(); return $this->collection->remove($key); } /** * {@inheritDoc} */ public function removeElement($element) { $this->initialize(); return $this->collection->removeElement($element); } /** * {@inheritDoc} */ public function containsKey($key) { $this->initialize(); return $this->collection->containsKey($key); } /** * {@inheritDoc} */ public function get($key) { $this->initialize(); return $this->collection->get($key); } /** * {@inheritDoc} */ public function getKeys() { $this->initialize(); return $this->collection->getKeys(); } /** * {@inheritDoc} */ public function getValues() { $this->initialize(); return $this->collection->getValues(); } /** * {@inheritDoc} */ public function set($key, $value) { $this->initialize(); $this->collection->set($key, $value); } /** * {@inheritDoc} */ public function toArray() { $this->initialize(); return $this->collection->toArray(); } /** * {@inheritDoc} */ public function first() { $this->initialize(); return $this->collection->first(); } /** * {@inheritDoc} */ public function last() { $this->initialize(); return $this->collection->last(); } /** * {@inheritDoc} */ public function key() { $this->initialize(); return $this->collection->key(); } /** * {@inheritDoc} */ public function current() { $this->initialize(); return $this->collection->current(); } /** * {@inheritDoc} */ public function next() { $this->initialize(); return $this->collection->next(); } /** * {@inheritDoc} */ public function exists(Closure $p) { $this->initialize(); return $this->collection->exists($p); } /** * {@inheritDoc} */ public function filter(Closure $p) { $this->initialize(); return $this->collection->filter($p); } /** * {@inheritDoc} */ public function forAll(Closure $p) { $this->initialize(); return $this->collection->forAll($p); } /** * {@inheritDoc} */ public function map(Closure $func) { $this->initialize(); return $this->collection->map($func); } /** * {@inheritDoc} */ public function partition(Closure $p) { $this->initialize(); return $this->collection->partition($p); } /** * {@inheritDoc} * * @template TMaybeContained */ public function indexOf($element) { $this->initialize(); return $this->collection->indexOf($element); } /** * {@inheritDoc} */ public function slice($offset, $length = null) { $this->initialize(); return $this->collection->slice($offset, $length); } /** * {@inheritDoc} * * @return Traversable<int|string, mixed> * @psalm-return Traversable<TKey,T> */ #[ReturnTypeWillChange] public function getIterator() { $this->initialize(); return $this->collection->getIterator(); } /** * @param TKey $offset * * @return bool */ #[ReturnTypeWillChange] public function offsetExists($offset) { $this->initialize(); return $this->collection->offsetExists($offset); } /** * @param TKey $offset * * @return mixed */ #[ReturnTypeWillChange] public function offsetGet($offset) { $this->initialize(); return $this->collection->offsetGet($offset); } /** * @param TKey|null $offset * @param T $value * * @return void */ #[ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->initialize(); $this->collection->offsetSet($offset, $value); } /** * @param TKey $offset * * @return void */ #[ReturnTypeWillChange] public function offsetUnset($offset) { $this->initialize(); $this->collection->offsetUnset($offset); } /** * Is the lazy collection already initialized? * * @return bool * * @psalm-assert-if-true Collection<TKey,T> $this->collection */ public function isInitialized() { return $this->initialized; } /** * Initialize the collection * * @return void * * @psalm-assert Collection<TKey,T> $this->collection */ protected function initialize() { if ($this->initialized) { return; } $this->doInitialize(); $this->initialized = true; if ($this->collection === null) { throw new LogicException('You must initialize the collection property in the doInitialize() method.'); } } /** * Do the initialization logic * * @return void */ abstract protected function doInitialize(); } Doctrine/Common/Collections/ArrayCollection.php 0000644 00000022602 15120240431 0015616 0 ustar 00 <?php namespace Doctrine\Common\Collections; use ArrayIterator; use Closure; use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; use ReturnTypeWillChange; use Traversable; use function array_filter; use function array_key_exists; use function array_keys; use function array_map; use function array_reverse; use function array_search; use function array_slice; use function array_values; use function count; use function current; use function end; use function in_array; use function key; use function next; use function reset; use function spl_object_hash; use function uasort; use const ARRAY_FILTER_USE_BOTH; /** * An ArrayCollection is a Collection implementation that wraps a regular PHP array. * * Warning: Using (un-)serialize() on a collection is not a supported use-case * and may break when we change the internals in the future. If you need to * serialize a collection use {@link toArray()} and reconstruct the collection * manually. * * @psalm-template TKey of array-key * @psalm-template T * @template-implements Collection<TKey,T> * @template-implements Selectable<TKey,T> * @psalm-consistent-constructor */ class ArrayCollection implements Collection, Selectable { /** * An array containing the entries of this collection. * * @psalm-var array<TKey,T> * @var mixed[] */ private $elements; /** * Initializes a new ArrayCollection. * * @param array $elements * @psalm-param array<TKey,T> $elements */ public function __construct(array $elements = []) { $this->elements = $elements; } /** * {@inheritDoc} */ public function toArray() { return $this->elements; } /** * {@inheritDoc} */ public function first() { return reset($this->elements); } /** * Creates a new instance from the specified elements. * * This method is provided for derived classes to specify how a new * instance should be created when constructor semantics have changed. * * @param array $elements Elements. * @psalm-param array<K,V> $elements * * @return static * @psalm-return static<K,V> * * @psalm-template K of array-key * @psalm-template V */ protected function createFrom(array $elements) { return new static($elements); } /** * {@inheritDoc} */ public function last() { return end($this->elements); } /** * {@inheritDoc} */ public function key() { return key($this->elements); } /** * {@inheritDoc} */ public function next() { return next($this->elements); } /** * {@inheritDoc} */ public function current() { return current($this->elements); } /** * {@inheritDoc} */ public function remove($key) { if (! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) { return null; } $removed = $this->elements[$key]; unset($this->elements[$key]); return $removed; } /** * {@inheritDoc} */ public function removeElement($element) { $key = array_search($element, $this->elements, true); if ($key === false) { return false; } unset($this->elements[$key]); return true; } /** * Required by interface ArrayAccess. * * @param TKey $offset * * @return bool */ #[ReturnTypeWillChange] public function offsetExists($offset) { return $this->containsKey($offset); } /** * Required by interface ArrayAccess. * * @param TKey $offset * * @return mixed */ #[ReturnTypeWillChange] public function offsetGet($offset) { return $this->get($offset); } /** * Required by interface ArrayAccess. * * @param TKey|null $offset * @param T $value * * @return void */ #[ReturnTypeWillChange] public function offsetSet($offset, $value) { if (! isset($offset)) { $this->add($value); return; } $this->set($offset, $value); } /** * Required by interface ArrayAccess. * * @param TKey $offset * * @return void */ #[ReturnTypeWillChange] public function offsetUnset($offset) { $this->remove($offset); } /** * {@inheritDoc} */ public function containsKey($key) { return isset($this->elements[$key]) || array_key_exists($key, $this->elements); } /** * {@inheritDoc} * * @template TMaybeContained */ public function contains($element) { return in_array($element, $this->elements, true); } /** * {@inheritDoc} */ public function exists(Closure $p) { foreach ($this->elements as $key => $element) { if ($p($key, $element)) { return true; } } return false; } /** * {@inheritDoc} * * @psalm-param TMaybeContained $element * * @psalm-return (TMaybeContained is T ? TKey|false : false) * * @template TMaybeContained */ public function indexOf($element) { return array_search($element, $this->elements, true); } /** * {@inheritDoc} */ public function get($key) { return $this->elements[$key] ?? null; } /** * {@inheritDoc} */ public function getKeys() { return array_keys($this->elements); } /** * {@inheritDoc} */ public function getValues() { return array_values($this->elements); } /** * {@inheritDoc} * * @return int */ #[ReturnTypeWillChange] public function count() { return count($this->elements); } /** * {@inheritDoc} */ public function set($key, $value) { $this->elements[$key] = $value; } /** * {@inheritDoc} * * @psalm-suppress InvalidPropertyAssignmentValue * * This breaks assumptions about the template type, but it would * be a backwards-incompatible change to remove this method */ public function add($element) { $this->elements[] = $element; return true; } /** * {@inheritDoc} */ public function isEmpty() { return empty($this->elements); } /** * {@inheritDoc} * * @return Traversable<int|string, mixed> * @psalm-return Traversable<TKey,T> */ #[ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->elements); } /** * {@inheritDoc} * * @psalm-param Closure(T):U $func * * @return static * @psalm-return static<TKey, U> * * @psalm-template U */ public function map(Closure $func) { return $this->createFrom(array_map($func, $this->elements)); } /** * {@inheritDoc} * * @return static * @psalm-return static<TKey,T> */ public function filter(Closure $p) { return $this->createFrom(array_filter($this->elements, $p, ARRAY_FILTER_USE_BOTH)); } /** * {@inheritDoc} */ public function forAll(Closure $p) { foreach ($this->elements as $key => $element) { if (! $p($key, $element)) { return false; } } return true; } /** * {@inheritDoc} */ public function partition(Closure $p) { $matches = $noMatches = []; foreach ($this->elements as $key => $element) { if ($p($key, $element)) { $matches[$key] = $element; } else { $noMatches[$key] = $element; } } return [$this->createFrom($matches), $this->createFrom($noMatches)]; } /** * Returns a string representation of this object. * * @return string */ public function __toString() { return self::class . '@' . spl_object_hash($this); } /** * {@inheritDoc} */ public function clear() { $this->elements = []; } /** * {@inheritDoc} */ public function slice($offset, $length = null) { return array_slice($this->elements, $offset, $length, true); } /** * {@inheritDoc} */ public function matching(Criteria $criteria) { $expr = $criteria->getWhereExpression(); $filtered = $this->elements; if ($expr) { $visitor = new ClosureExpressionVisitor(); $filter = $visitor->dispatch($expr); $filtered = array_filter($filtered, $filter); } $orderings = $criteria->getOrderings(); if ($orderings) { $next = null; foreach (array_reverse($orderings) as $field => $ordering) { $next = ClosureExpressionVisitor::sortByField($field, $ordering === Criteria::DESC ? -1 : 1, $next); } uasort($filtered, $next); } $offset = $criteria->getFirstResult(); $length = $criteria->getMaxResults(); if ($offset || $length) { $filtered = array_slice($filtered, (int) $offset, $length); } return $this->createFrom($filtered); } } Doctrine/Common/Collections/Collection.php 0000644 00000006175 15120240431 0014626 0 ustar 00 <?php namespace Doctrine\Common\Collections; use ArrayAccess; use Closure; /** * The missing (SPL) Collection/Array/OrderedMap interface. * * A Collection resembles the nature of a regular PHP array. That is, * it is essentially an <b>ordered map</b> that can also be used * like a list. * * A Collection has an internal iterator just like a PHP array. In addition, * a Collection can be iterated with external iterators, which is preferable. * To use an external iterator simply use the foreach language construct to * iterate over the collection (which calls {@link getIterator()} internally) or * explicitly retrieve an iterator though {@link getIterator()} which can then be * used to iterate over the collection. * You can not rely on the internal iterator of the collection being at a certain * position unless you explicitly positioned it before. Prefer iteration with * external iterators. * * @psalm-template TKey of array-key * @psalm-template T * @template-extends ReadableCollection<TKey, T> * @template-extends ArrayAccess<TKey, T> */ interface Collection extends ReadableCollection, ArrayAccess { /** * Adds an element at the end of the collection. * * @param mixed $element The element to add. * @psalm-param T $element * * @return true Always TRUE. */ public function add($element); /** * Clears the collection, removing all elements. * * @return void */ public function clear(); /** * Removes the element at the specified index from the collection. * * @param string|int $key The key/index of the element to remove. * @psalm-param TKey $key * * @return mixed The removed element or NULL, if the collection did not contain the element. * @psalm-return T|null */ public function remove($key); /** * Removes the specified element from the collection, if it is found. * * @param mixed $element The element to remove. * @psalm-param T $element * * @return bool TRUE if this collection contained the specified element, FALSE otherwise. */ public function removeElement($element); /** * Sets an element in the collection at the specified key/index. * * @param string|int $key The key/index of the element to set. * @param mixed $value The element to set. * @psalm-param TKey $key * @psalm-param T $value * * @return void */ public function set($key, $value); /** * {@inheritdoc} * * @return Collection<mixed> A collection with the results of the filter operation. * @psalm-return Collection<TKey, T> */ public function filter(Closure $p); /** * {@inheritdoc} * * @return Collection<mixed>[] An array with two elements. The first element contains the collection * of elements where the predicate returned TRUE, the second element * contains the collection of elements where the predicate returned FALSE. * @psalm-return array{0: Collection<TKey, T>, 1: Collection<TKey, T>} */ public function partition(Closure $p); } Doctrine/Common/Collections/Criteria.php 0000644 00000013002 15120240431 0014260 0 ustar 00 <?php namespace Doctrine\Common\Collections; use Doctrine\Common\Collections\Expr\CompositeExpression; use Doctrine\Common\Collections\Expr\Expression; use Doctrine\Deprecations\Deprecation; use function array_map; use function func_num_args; use function strtoupper; /** * Criteria for filtering Selectable collections. * * @psalm-consistent-constructor */ class Criteria { public const ASC = 'ASC'; public const DESC = 'DESC'; /** @var ExpressionBuilder|null */ private static $expressionBuilder; /** @var Expression|null */ private $expression; /** @var string[] */ private $orderings = []; /** @var int|null */ private $firstResult; /** @var int|null */ private $maxResults; /** * Creates an instance of the class. * * @return Criteria */ public static function create() { return new static(); } /** * Returns the expression builder. * * @return ExpressionBuilder */ public static function expr() { if (self::$expressionBuilder === null) { self::$expressionBuilder = new ExpressionBuilder(); } return self::$expressionBuilder; } /** * Construct a new Criteria. * * @param string[]|null $orderings * @param int|null $firstResult * @param int|null $maxResults */ public function __construct(?Expression $expression = null, ?array $orderings = null, $firstResult = null, $maxResults = null) { $this->expression = $expression; if ($firstResult === null && func_num_args() > 2) { Deprecation::trigger( 'doctrine/collections', 'https://github.com/doctrine/collections/pull/311', 'Passing null as $firstResult to the constructor of %s is deprecated. Pass 0 instead or omit the argument.', self::class ); } $this->setFirstResult($firstResult); $this->setMaxResults($maxResults); if ($orderings === null) { return; } $this->orderBy($orderings); } /** * Sets the where expression to evaluate when this Criteria is searched for. * * @return $this */ public function where(Expression $expression) { $this->expression = $expression; return $this; } /** * Appends the where expression to evaluate when this Criteria is searched for * using an AND with previous expression. * * @return $this */ public function andWhere(Expression $expression) { if ($this->expression === null) { return $this->where($expression); } $this->expression = new CompositeExpression( CompositeExpression::TYPE_AND, [$this->expression, $expression] ); return $this; } /** * Appends the where expression to evaluate when this Criteria is searched for * using an OR with previous expression. * * @return $this */ public function orWhere(Expression $expression) { if ($this->expression === null) { return $this->where($expression); } $this->expression = new CompositeExpression( CompositeExpression::TYPE_OR, [$this->expression, $expression] ); return $this; } /** * Gets the expression attached to this Criteria. * * @return Expression|null */ public function getWhereExpression() { return $this->expression; } /** * Gets the current orderings of this Criteria. * * @return string[] */ public function getOrderings() { return $this->orderings; } /** * Sets the ordering of the result of this Criteria. * * Keys are field and values are the order, being either ASC or DESC. * * @see Criteria::ASC * @see Criteria::DESC * * @param string[] $orderings * * @return $this */ public function orderBy(array $orderings) { $this->orderings = array_map( static function (string $ordering): string { return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC; }, $orderings ); return $this; } /** * Gets the current first result option of this Criteria. * * @return int|null */ public function getFirstResult() { return $this->firstResult; } /** * Set the number of first result that this Criteria should return. * * @param int|null $firstResult The value to set. * * @return $this */ public function setFirstResult($firstResult) { if ($firstResult === null) { Deprecation::triggerIfCalledFromOutside( 'doctrine/collections', 'https://github.com/doctrine/collections/pull/311', 'Passing null to %s() is deprecated, pass 0 instead.', __METHOD__ ); } $this->firstResult = $firstResult; return $this; } /** * Gets maxResults. * * @return int|null */ public function getMaxResults() { return $this->maxResults; } /** * Sets maxResults. * * @param int|null $maxResults The value to set. * * @return $this */ public function setMaxResults($maxResults) { $this->maxResults = $maxResults; return $this; } } Doctrine/Common/Collections/ExpressionBuilder.php 0000644 00000007701 15120240431 0016175 0 ustar 00 <?php namespace Doctrine\Common\Collections; use Doctrine\Common\Collections\Expr\Comparison; use Doctrine\Common\Collections\Expr\CompositeExpression; use Doctrine\Common\Collections\Expr\Value; use function func_get_args; /** * Builder for Expressions in the {@link Selectable} interface. * * Important Notice for interoperable code: You have to use scalar * values only for comparisons, otherwise the behavior of the comparison * may be different between implementations (Array vs ORM vs ODM). */ class ExpressionBuilder { /** * @param mixed ...$x * * @return CompositeExpression */ public function andX($x = null) { return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); } /** * @param mixed ...$x * * @return CompositeExpression */ public function orX($x = null) { return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function eq($field, $value) { return new Comparison($field, Comparison::EQ, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function gt($field, $value) { return new Comparison($field, Comparison::GT, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function lt($field, $value) { return new Comparison($field, Comparison::LT, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function gte($field, $value) { return new Comparison($field, Comparison::GTE, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function lte($field, $value) { return new Comparison($field, Comparison::LTE, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function neq($field, $value) { return new Comparison($field, Comparison::NEQ, new Value($value)); } /** * @param string $field * * @return Comparison */ public function isNull($field) { return new Comparison($field, Comparison::EQ, new Value(null)); } /** * @param string $field * @param mixed[] $values * * @return Comparison */ public function in($field, array $values) { return new Comparison($field, Comparison::IN, new Value($values)); } /** * @param string $field * @param mixed[] $values * * @return Comparison */ public function notIn($field, array $values) { return new Comparison($field, Comparison::NIN, new Value($values)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function contains($field, $value) { return new Comparison($field, Comparison::CONTAINS, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function memberOf($field, $value) { return new Comparison($field, Comparison::MEMBER_OF, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function startsWith($field, $value) { return new Comparison($field, Comparison::STARTS_WITH, new Value($value)); } /** * @param string $field * @param mixed $value * * @return Comparison */ public function endsWith($field, $value) { return new Comparison($field, Comparison::ENDS_WITH, new Value($value)); } } Doctrine/Common/Collections/ReadableCollection.php 0000644 00000015122 15120240431 0016236 0 ustar 00 <?php namespace Doctrine\Common\Collections; use Closure; use Countable; use IteratorAggregate; /** * @psalm-template TKey of array-key * @template-covariant T * @template-extends IteratorAggregate<TKey, T> */ interface ReadableCollection extends Countable, IteratorAggregate { /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @param mixed $element The element to search for. * @psalm-param TMaybeContained $element * * @return bool TRUE if the collection contains the element, FALSE otherwise. * @psalm-return (TMaybeContained is T ? bool : false) * * @template TMaybeContained */ public function contains($element); /** * Checks whether the collection is empty (contains no elements). * * @return bool TRUE if the collection is empty, FALSE otherwise. */ public function isEmpty(); /** * Checks whether the collection contains an element with the specified key/index. * * @param string|int $key The key/index to check for. * @psalm-param TKey $key * * @return bool TRUE if the collection contains an element with the specified key/index, * FALSE otherwise. */ public function containsKey($key); /** * Gets the element at the specified key/index. * * @param string|int $key The key/index of the element to retrieve. * @psalm-param TKey $key * * @return mixed * @psalm-return T|null */ public function get($key); /** * Gets all keys/indices of the collection. * * @return int[]|string[] The keys/indices of the collection, in the order of the corresponding * elements in the collection. * @psalm-return list<TKey> */ public function getKeys(); /** * Gets all values of the collection. * * @return mixed[] The values of all elements in the collection, in the * order they appear in the collection. * @psalm-return list<T> */ public function getValues(); /** * Gets a native PHP array representation of the collection. * * @return mixed[] * @psalm-return array<TKey,T> */ public function toArray(); /** * Sets the internal iterator to the first element in the collection and returns this element. * * @return mixed * @psalm-return T|false */ public function first(); /** * Sets the internal iterator to the last element in the collection and returns this element. * * @return mixed * @psalm-return T|false */ public function last(); /** * Gets the key/index of the element at the current iterator position. * * @return int|string|null * @psalm-return TKey|null */ public function key(); /** * Gets the element of the collection at the current iterator position. * * @return mixed * @psalm-return T|false */ public function current(); /** * Moves the internal iterator position to the next element and returns this element. * * @return mixed * @psalm-return T|false */ public function next(); /** * Extracts a slice of $length elements starting at position $offset from the Collection. * * If $length is null it returns all elements from $offset to the end of the Collection. * Keys have to be preserved by this method. Calling this method will only return the * selected slice and NOT change the elements contained in the collection slice is called on. * * @param int $offset The offset to start from. * @param int|null $length The maximum number of elements to return, or null for no limit. * * @return mixed[] * @psalm-return array<TKey,T> */ public function slice($offset, $length = null); /** * Tests for the existence of an element that satisfies the given predicate. * * @param Closure $p The predicate. * @psalm-param Closure(TKey, T):bool $p * * @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise. */ public function exists(Closure $p); /** * Returns all the elements of this collection that satisfy the predicate p. * The order of the elements is preserved. * * @param Closure $p The predicate used for filtering. * @psalm-param Closure(T):bool $p * * @return ReadableCollection<mixed> A collection with the results of the filter operation. * @psalm-return ReadableCollection<TKey, T> */ public function filter(Closure $p); /** * Applies the given function to each element in the collection and returns * a new collection with the elements returned by the function. * * @psalm-param Closure(T):U $func * * @return Collection<mixed> * @psalm-return Collection<TKey, U> * * @psalm-template U */ public function map(Closure $func); /** * Partitions this collection in two collections according to a predicate. * Keys are preserved in the resulting collections. * * @param Closure $p The predicate on which to partition. * @psalm-param Closure(TKey, T):bool $p * * @return ReadableCollection<mixed>[] An array with two elements. The first element contains the collection * of elements where the predicate returned TRUE, the second element * contains the collection of elements where the predicate returned FALSE. * @psalm-return array{0: ReadableCollection<TKey, T>, 1: ReadableCollection<TKey, T>} */ public function partition(Closure $p); /** * Tests whether the given predicate p holds for all elements of this collection. * * @param Closure $p The predicate. * @psalm-param Closure(TKey, T):bool $p * * @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. */ public function forAll(Closure $p); /** * Gets the index/key of a given element. The comparison of two elements is strict, * that means not only the value but also the type must match. * For objects this means reference equality. * * @param mixed $element The element to search for. * @psalm-param TMaybeContained $element * * @return int|string|bool The key/index of the element or FALSE if the element was not found. * @psalm-return (TMaybeContained is T ? TKey|false : false) * * @template TMaybeContained */ public function indexOf($element); } Doctrine/Common/Collections/Selectable.php 0000644 00000002012 15120240431 0014560 0 ustar 00 <?php namespace Doctrine\Common\Collections; /** * Interface for collections that allow efficient filtering with an expression API. * * Goal of this interface is a backend independent method to fetch elements * from a collections. {@link Expression} is crafted in a way that you can * implement queries from both in-memory and database-backed collections. * * For database backed collections this allows very efficient access by * utilizing the query APIs, for example SQL in the ORM. Applications using * this API can implement efficient database access without having to ask the * EntityManager or Repositories. * * @psalm-template TKey as array-key * @psalm-template T */ interface Selectable { /** * Selects all elements from a selectable that match the expression and * returns a new collection containing these elements. * * @return Collection<mixed>&Selectable<mixed> * @psalm-return Collection<TKey,T>&Selectable<TKey,T> */ public function matching(Criteria $criteria); } Doctrine/Common/Annotations/Annotation/Attribute.php 0000644 00000000547 15120334141 0016626 0 ustar 00 <?php namespace Doctrine\Common\Annotations\Annotation; /** * Annotation that can be used to signal to the parser * to check the attribute type during the parsing process. * * @Annotation */ final class Attribute { /** @var string */ public $name; /** @var string */ public $type; /** @var bool */ public $required = false; } Doctrine/Common/Annotations/Annotation/Attributes.php 0000644 00000000447 15120334141 0017010 0 ustar 00 <?php namespace Doctrine\Common\Annotations\Annotation; /** * Annotation that can be used to signal to the parser * to check the types of all declared attributes during the parsing process. * * @Annotation */ final class Attributes { /** @var array<Attribute> */ public $value; } Doctrine/Common/Annotations/Annotation/Enum.php 0000644 00000003312 15120334141 0015560 0 ustar 00 <?php namespace Doctrine\Common\Annotations\Annotation; use InvalidArgumentException; use function get_class; use function gettype; use function in_array; use function is_object; use function is_scalar; use function sprintf; /** * Annotation that can be used to signal to the parser * to check the available values during the parsing process. * * @Annotation * @Attributes({ * @Attribute("value", required = true, type = "array"), * @Attribute("literal", required = false, type = "array") * }) */ final class Enum { /** @phpstan-var list<scalar> */ public $value; /** * Literal target declaration. * * @var mixed[] */ public $literal; /** * @phpstan-param array{literal?: mixed[], value: list<scalar>} $values * * @throws InvalidArgumentException */ public function __construct(array $values) { if (! isset($values['literal'])) { $values['literal'] = []; } foreach ($values['value'] as $var) { if (! is_scalar($var)) { throw new InvalidArgumentException(sprintf( '@Enum supports only scalar values "%s" given.', is_object($var) ? get_class($var) : gettype($var) )); } } foreach ($values['literal'] as $key => $var) { if (! in_array($key, $values['value'])) { throw new InvalidArgumentException(sprintf( 'Undefined enumerator value "%s" for literal "%s".', $key, $var )); } } $this->value = $values['value']; $this->literal = $values['literal']; } } Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php 0000644 00000001752 15120334141 0020140 0 ustar 00 <?php namespace Doctrine\Common\Annotations\Annotation; use RuntimeException; use function is_array; use function is_string; use function json_encode; use function sprintf; /** * Annotation that can be used to signal to the parser to ignore specific * annotations during the parsing process. * * @Annotation */ final class IgnoreAnnotation { /** @phpstan-var list<string> */ public $names; /** * @phpstan-param array{value: string|list<string>} $values * * @throws RuntimeException */ public function __construct(array $values) { if (is_string($values['value'])) { $values['value'] = [$values['value']]; } if (! is_array($values['value'])) { throw new RuntimeException(sprintf( '@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value']) )); } $this->names = $values['value']; } } Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.php 0000644 00000000371 15120334141 0021653 0 ustar 00 <?php namespace Doctrine\Common\Annotations\Annotation; /** * Annotation that indicates that the annotated class should be constructed with a named argument call. * * @Annotation * @Target("CLASS") */ final class NamedArgumentConstructor { } Doctrine/Common/Annotations/Annotation/Required.php 0000644 00000000352 15120334141 0016435 0 ustar 00 <?php namespace Doctrine\Common\Annotations\Annotation; /** * Annotation that can be used to signal to the parser * to check if that attribute is required during the parsing process. * * @Annotation */ final class Required { } Doctrine/Common/Annotations/Annotation/Target.php 0000644 00000005112 15120334141 0016102 0 ustar 00 <?php namespace Doctrine\Common\Annotations\Annotation; use InvalidArgumentException; use function array_keys; use function get_class; use function gettype; use function implode; use function is_array; use function is_object; use function is_string; use function sprintf; /** * Annotation that can be used to signal to the parser * to check the annotation target during the parsing process. * * @Annotation */ final class Target { public const TARGET_CLASS = 1; public const TARGET_METHOD = 2; public const TARGET_PROPERTY = 4; public const TARGET_ANNOTATION = 8; public const TARGET_FUNCTION = 16; public const TARGET_ALL = 31; /** @var array<string, int> */ private static $map = [ 'ALL' => self::TARGET_ALL, 'CLASS' => self::TARGET_CLASS, 'METHOD' => self::TARGET_METHOD, 'PROPERTY' => self::TARGET_PROPERTY, 'FUNCTION' => self::TARGET_FUNCTION, 'ANNOTATION' => self::TARGET_ANNOTATION, ]; /** @phpstan-var list<string> */ public $value; /** * Targets as bitmask. * * @var int */ public $targets; /** * Literal target declaration. * * @var string */ public $literal; /** * @phpstan-param array{value?: string|list<string>} $values * * @throws InvalidArgumentException */ public function __construct(array $values) { if (! isset($values['value'])) { $values['value'] = null; } if (is_string($values['value'])) { $values['value'] = [$values['value']]; } if (! is_array($values['value'])) { throw new InvalidArgumentException( sprintf( '@Target expects either a string value, or an array of strings, "%s" given.', is_object($values['value']) ? get_class($values['value']) : gettype($values['value']) ) ); } $bitmask = 0; foreach ($values['value'] as $literal) { if (! isset(self::$map[$literal])) { throw new InvalidArgumentException( sprintf( 'Invalid Target "%s". Available targets: [%s]', $literal, implode(', ', array_keys(self::$map)) ) ); } $bitmask |= self::$map[$literal]; } $this->targets = $bitmask; $this->value = $values['value']; $this->literal = implode(', ', $this->value); } } Doctrine/Common/Annotations/Annotation.php 0000644 00000002452 15120334141 0014660 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use BadMethodCallException; use function sprintf; /** * Annotations class. */ class Annotation { /** * Value property. Common among all derived classes. * * @var mixed */ public $value; /** @param array<string, mixed> $data Key-value for properties to be defined in this class. */ final public function __construct(array $data) { foreach ($data as $key => $value) { $this->$key = $value; } } /** * Error handler for unknown property accessor in Annotation class. * * @param string $name Unknown property name. * * @throws BadMethodCallException */ public function __get($name) { throw new BadMethodCallException( sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class) ); } /** * Error handler for unknown property mutator in Annotation class. * * @param string $name Unknown property name. * @param mixed $value Property value. * * @throws BadMethodCallException */ public function __set($name, $value) { throw new BadMethodCallException( sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class) ); } } Doctrine/Common/Annotations/AnnotationException.php 0000644 00000011063 15120334141 0016535 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use Exception; use Throwable; use function get_class; use function gettype; use function implode; use function is_object; use function sprintf; /** * Description of AnnotationException */ class AnnotationException extends Exception { /** * Creates a new AnnotationException describing a Syntax error. * * @param string $message Exception message * * @return AnnotationException */ public static function syntaxError($message) { return new self('[Syntax Error] ' . $message); } /** * Creates a new AnnotationException describing a Semantical error. * * @param string $message Exception message * * @return AnnotationException */ public static function semanticalError($message) { return new self('[Semantical Error] ' . $message); } /** * Creates a new AnnotationException describing an error which occurred during * the creation of the annotation. * * @param string $message * * @return AnnotationException */ public static function creationError($message, ?Throwable $previous = null) { return new self('[Creation Error] ' . $message, 0, $previous); } /** * Creates a new AnnotationException describing a type error. * * @param string $message * * @return AnnotationException */ public static function typeError($message) { return new self('[Type Error] ' . $message); } /** * Creates a new AnnotationException describing a constant semantical error. * * @param string $identifier * @param string $context * * @return AnnotationException */ public static function semanticalErrorConstants($identifier, $context = null) { return self::semanticalError(sprintf( "Couldn't find constant %s%s.", $identifier, $context ? ', ' . $context : '' )); } /** * Creates a new AnnotationException describing an type error of an attribute. * * @param string $attributeName * @param string $annotationName * @param string $context * @param string $expected * @param mixed $actual * * @return AnnotationException */ public static function attributeTypeError($attributeName, $annotationName, $context, $expected, $actual) { return self::typeError(sprintf( 'Attribute "%s" of @%s declared on %s expects %s, but got %s.', $attributeName, $annotationName, $context, $expected, is_object($actual) ? 'an instance of ' . get_class($actual) : gettype($actual) )); } /** * Creates a new AnnotationException describing an required error of an attribute. * * @param string $attributeName * @param string $annotationName * @param string $context * @param string $expected * * @return AnnotationException */ public static function requiredError($attributeName, $annotationName, $context, $expected) { return self::typeError(sprintf( 'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.', $attributeName, $annotationName, $context, $expected )); } /** * Creates a new AnnotationException describing a invalid enummerator. * * @param string $attributeName * @param string $annotationName * @param string $context * @param mixed $given * @phpstan-param list<string> $available * * @return AnnotationException */ public static function enumeratorError($attributeName, $annotationName, $context, $available, $given) { return new self(sprintf( '[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.', $attributeName, $annotationName, $context, implode(', ', $available), is_object($given) ? get_class($given) : $given )); } /** @return AnnotationException */ public static function optimizerPlusSaveComments() { return new self( 'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.' ); } /** @return AnnotationException */ public static function optimizerPlusLoadComments() { return new self( 'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.' ); } } Doctrine/Common/Annotations/AnnotationReader.php 0000644 00000026371 15120334141 0016011 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; use Doctrine\Common\Annotations\Annotation\Target; use ReflectionClass; use ReflectionFunction; use ReflectionMethod; use ReflectionProperty; use function array_merge; use function class_exists; use function extension_loaded; use function ini_get; /** * A reader for docblock annotations. */ class AnnotationReader implements Reader { /** * Global map for imports. * * @var array<string, class-string> */ private static $globalImports = [ 'ignoreannotation' => Annotation\IgnoreAnnotation::class, ]; /** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names are case sensitive. * * @var array<string, true> */ private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST; /** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names are case sensitive. * * @var array<string, true> */ private static $globalIgnoredNamespaces = []; /** * Add a new annotation to the globally ignored annotation names with regard to exception handling. * * @param string $name */ public static function addGlobalIgnoredName($name) { self::$globalIgnoredNames[$name] = true; } /** * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling. * * @param string $namespace */ public static function addGlobalIgnoredNamespace($namespace) { self::$globalIgnoredNamespaces[$namespace] = true; } /** * Annotations parser. * * @var DocParser */ private $parser; /** * Annotations parser used to collect parsing metadata. * * @var DocParser */ private $preParser; /** * PHP parser used to collect imports. * * @var PhpParser */ private $phpParser; /** * In-memory cache mechanism to store imported annotations per class. * * @psalm-var array<'class'|'function', array<string, array<string, class-string>>> */ private $imports = []; /** * In-memory cache mechanism to store ignored annotations per class. * * @psalm-var array<'class'|'function', array<string, array<string, true>>> */ private $ignoredAnnotationNames = []; /** * Initializes a new AnnotationReader. * * @throws AnnotationException */ public function __construct(?DocParser $parser = null) { if ( extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' || ini_get('opcache.save_comments') === '0') ) { throw AnnotationException::optimizerPlusSaveComments(); } if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) { throw AnnotationException::optimizerPlusSaveComments(); } // Make sure that the IgnoreAnnotation annotation is loaded class_exists(IgnoreAnnotation::class); $this->parser = $parser ?: new DocParser(); $this->preParser = new DocParser(); $this->preParser->setImports(self::$globalImports); $this->preParser->setIgnoreNotImportedAnnotations(true); $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames); $this->phpParser = new PhpParser(); } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $this->parser->setTarget(Target::TARGET_CLASS); $this->parser->setImports($this->getImports($class)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { $annotations = $this->getClassAnnotations($class); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $context = 'property ' . $class->getName() . '::$' . $property->getName(); $this->parser->setTarget(Target::TARGET_PROPERTY); $this->parser->setImports($this->getPropertyImports($property)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($property->getDocComment(), $context); } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { $annotations = $this->getPropertyAnnotations($property); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; $this->parser->setTarget(Target::TARGET_METHOD); $this->parser->setImports($this->getMethodImports($method)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($method->getDocComment(), $context); } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { $annotations = $this->getMethodAnnotations($method); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * Gets the annotations applied to a function. * * @phpstan-return list<object> An array of Annotations. */ public function getFunctionAnnotations(ReflectionFunction $function): array { $context = 'function ' . $function->getName(); $this->parser->setTarget(Target::TARGET_FUNCTION); $this->parser->setImports($this->getImports($function)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($function->getDocComment(), $context); } /** * Gets a function annotation. * * @return object|null The Annotation or NULL, if the requested annotation does not exist. */ public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName) { $annotations = $this->getFunctionAnnotations($function); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * Returns the ignored annotations for the given class or function. * * @param ReflectionClass|ReflectionFunction $reflection * * @return array<string, true> */ private function getIgnoredAnnotationNames($reflection): array { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName(); if (isset($this->ignoredAnnotationNames[$type][$name])) { return $this->ignoredAnnotationNames[$type][$name]; } $this->collectParsingMetadata($reflection); return $this->ignoredAnnotationNames[$type][$name]; } /** * Retrieves imports for a class or a function. * * @param ReflectionClass|ReflectionFunction $reflection * * @return array<string, class-string> */ private function getImports($reflection): array { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName(); if (isset($this->imports[$type][$name])) { return $this->imports[$type][$name]; } $this->collectParsingMetadata($reflection); return $this->imports[$type][$name]; } /** * Retrieves imports for methods. * * @return array<string, class-string> */ private function getMethodImports(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $classImports = $this->getImports($class); $traitImports = []; foreach ($class->getTraits() as $trait) { if ( ! $trait->hasMethod($method->getName()) || $trait->getFileName() !== $method->getFileName() ) { continue; } $traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait)); } return array_merge($classImports, $traitImports); } /** * Retrieves imports for properties. * * @return array<string, class-string> */ private function getPropertyImports(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $classImports = $this->getImports($class); $traitImports = []; foreach ($class->getTraits() as $trait) { if (! $trait->hasProperty($property->getName())) { continue; } $traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait)); } return array_merge($classImports, $traitImports); } /** * Collects parsing metadata for a given class or function. * * @param ReflectionClass|ReflectionFunction $reflection */ private function collectParsingMetadata($reflection): void { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName(); $ignoredAnnotationNames = self::$globalIgnoredNames; $annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name); foreach ($annotations as $annotation) { if (! ($annotation instanceof IgnoreAnnotation)) { continue; } foreach ($annotation->names as $annot) { $ignoredAnnotationNames[$annot] = true; } } $this->imports[$type][$name] = array_merge( self::$globalImports, $this->phpParser->parseUseStatements($reflection), [ '__NAMESPACE__' => $reflection->getNamespaceName(), 'self' => $name, ] ); $this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames; } } Doctrine/Common/Annotations/AnnotationRegistry.php 0000644 00000013121 15120334141 0016404 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use function array_key_exists; use function array_merge; use function class_exists; use function in_array; use function is_file; use function str_replace; use function stream_resolve_include_path; use function strpos; use const DIRECTORY_SEPARATOR; final class AnnotationRegistry { /** * A map of namespaces to use for autoloading purposes based on a PSR-0 convention. * * Contains the namespace as key and an array of directories as value. If the value is NULL * the include path is used for checking for the corresponding file. * * This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own. * * @var string[][]|string[]|null[] */ private static $autoloadNamespaces = []; /** * A map of autoloader callables. * * @var callable[] */ private static $loaders = []; /** * An array of classes which cannot be found * * @var null[] indexed by class name */ private static $failedToAutoload = []; /** * Whenever registerFile() was used. Disables use of standard autoloader. * * @var bool */ private static $registerFileUsed = false; public static function reset(): void { self::$autoloadNamespaces = []; self::$loaders = []; self::$failedToAutoload = []; self::$registerFileUsed = false; } /** * Registers file. * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. */ public static function registerFile(string $file): void { self::$registerFileUsed = true; require_once $file; } /** * Adds a namespace with one or many directories to look for files or null for the include path. * * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. * * @phpstan-param string|list<string>|null $dirs */ public static function registerAutoloadNamespace(string $namespace, $dirs = null): void { self::$autoloadNamespaces[$namespace] = $dirs; } /** * Registers multiple namespaces. * * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. * * @param string[][]|string[]|null[] $namespaces indexed by namespace name */ public static function registerAutoloadNamespaces(array $namespaces): void { self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); } /** * Registers an autoloading callable for annotations, much like spl_autoload_register(). * * NOTE: These class loaders HAVE to be silent when a class was not found! * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class. * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. */ public static function registerLoader(callable $callable): void { // Reset our static cache now that we have a new loader to work with self::$failedToAutoload = []; self::$loaders[] = $callable; } /** * Registers an autoloading callable for annotations, if it is not already registered * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. */ public static function registerUniqueLoader(callable $callable): void { if (in_array($callable, self::$loaders, true)) { return; } self::registerLoader($callable); } /** * Autoloads an annotation class silently. */ public static function loadAnnotationClass(string $class): bool { if (class_exists($class, false)) { return true; } if (array_key_exists($class, self::$failedToAutoload)) { return false; } foreach (self::$autoloadNamespaces as $namespace => $dirs) { if (strpos($class, $namespace) !== 0) { continue; } $file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'; if ($dirs === null) { $path = stream_resolve_include_path($file); if ($path) { require $path; return true; } } else { foreach ((array) $dirs as $dir) { if (is_file($dir . DIRECTORY_SEPARATOR . $file)) { require $dir . DIRECTORY_SEPARATOR . $file; return true; } } } } foreach (self::$loaders as $loader) { if ($loader($class) === true) { return true; } } if ( self::$loaders === [] && self::$autoloadNamespaces === [] && self::$registerFileUsed === false && class_exists($class) ) { return true; } self::$failedToAutoload[$class] = null; return false; } } Doctrine/Common/Annotations/CachedReader.php 0000644 00000016122 15120334141 0015037 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use Doctrine\Common\Cache\Cache; use ReflectionClass; use ReflectionMethod; use ReflectionProperty; use function array_map; use function array_merge; use function assert; use function filemtime; use function max; use function time; /** * A cache aware annotation reader. * * @deprecated the CachedReader is deprecated and will be removed * in version 2.0.0 of doctrine/annotations. Please use the * {@see \Doctrine\Common\Annotations\PsrCachedReader} instead. */ final class CachedReader implements Reader { /** @var Reader */ private $delegate; /** @var Cache */ private $cache; /** @var bool */ private $debug; /** @var array<string, array<object>> */ private $loadedAnnotations = []; /** @var int[] */ private $loadedFilemtimes = []; /** @param bool $debug */ public function __construct(Reader $reader, Cache $cache, $debug = false) { $this->delegate = $reader; $this->cache = $cache; $this->debug = (bool) $debug; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $cacheKey = $class->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class); if ($annots === false) { $annots = $this->delegate->getClassAnnotations($class); $this->saveToCache($cacheKey, $annots); } return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { foreach ($this->getClassAnnotations($class) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $cacheKey = $class->getName() . '$' . $property->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class); if ($annots === false) { $annots = $this->delegate->getPropertyAnnotations($property); $this->saveToCache($cacheKey, $annots); } return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { foreach ($this->getPropertyAnnotations($property) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $cacheKey = $class->getName() . '#' . $method->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class); if ($annots === false) { $annots = $this->delegate->getMethodAnnotations($method); $this->saveToCache($cacheKey, $annots); } return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { foreach ($this->getMethodAnnotations($method) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * Clears loaded annotations. * * @return void */ public function clearLoadedAnnotations() { $this->loadedAnnotations = []; $this->loadedFilemtimes = []; } /** * Fetches a value from the cache. * * @param string $cacheKey The cache key. * * @return mixed The cached value or false when the value is not in cache. */ private function fetchFromCache($cacheKey, ReflectionClass $class) { $data = $this->cache->fetch($cacheKey); if ($data !== false) { if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) { return $data; } } return false; } /** * Saves a value to the cache. * * @param string $cacheKey The cache key. * @param mixed $value The value. * * @return void */ private function saveToCache($cacheKey, $value) { $this->cache->save($cacheKey, $value); if (! $this->debug) { return; } $this->cache->save('[C]' . $cacheKey, time()); } /** * Checks if the cache is fresh. * * @param string $cacheKey * * @return bool */ private function isCacheFresh($cacheKey, ReflectionClass $class) { $lastModification = $this->getLastModification($class); if ($lastModification === 0) { return true; } return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification; } /** * Returns the time the class was last modified, testing traits and parents */ private function getLastModification(ReflectionClass $class): int { $filename = $class->getFileName(); if (isset($this->loadedFilemtimes[$filename])) { return $this->loadedFilemtimes[$filename]; } $parent = $class->getParentClass(); $lastModification = max(array_merge( [$filename ? filemtime($filename) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $class->getTraits()), array_map(function (ReflectionClass $class): int { return $this->getLastModification($class); }, $class->getInterfaces()), $parent ? [$this->getLastModification($parent)] : [] )); assert($lastModification !== false); return $this->loadedFilemtimes[$filename] = $lastModification; } private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int { $fileName = $reflectionTrait->getFileName(); if (isset($this->loadedFilemtimes[$fileName])) { return $this->loadedFilemtimes[$fileName]; } $lastModificationTime = max(array_merge( [$fileName ? filemtime($fileName) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $reflectionTrait->getTraits()) )); assert($lastModificationTime !== false); return $this->loadedFilemtimes[$fileName] = $lastModificationTime; } } Doctrine/Common/Annotations/DocLexer.php 0000644 00000007400 15120334141 0014251 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use Doctrine\Common\Lexer\AbstractLexer; use function ctype_alpha; use function is_numeric; use function str_replace; use function stripos; use function strlen; use function strpos; use function strtolower; use function substr; /** * Simple lexer for docblock annotations. * * @template-extends AbstractLexer<DocLexer::T_*, string> */ final class DocLexer extends AbstractLexer { public const T_NONE = 1; public const T_INTEGER = 2; public const T_STRING = 3; public const T_FLOAT = 4; // All tokens that are also identifiers should be >= 100 public const T_IDENTIFIER = 100; public const T_AT = 101; public const T_CLOSE_CURLY_BRACES = 102; public const T_CLOSE_PARENTHESIS = 103; public const T_COMMA = 104; public const T_EQUALS = 105; public const T_FALSE = 106; public const T_NAMESPACE_SEPARATOR = 107; public const T_OPEN_CURLY_BRACES = 108; public const T_OPEN_PARENTHESIS = 109; public const T_TRUE = 110; public const T_NULL = 111; public const T_COLON = 112; public const T_MINUS = 113; /** @var array<string, self::T*> */ protected $noCase = [ '@' => self::T_AT, ',' => self::T_COMMA, '(' => self::T_OPEN_PARENTHESIS, ')' => self::T_CLOSE_PARENTHESIS, '{' => self::T_OPEN_CURLY_BRACES, '}' => self::T_CLOSE_CURLY_BRACES, '=' => self::T_EQUALS, ':' => self::T_COLON, '-' => self::T_MINUS, '\\' => self::T_NAMESPACE_SEPARATOR, ]; /** @var array<string, self::T*> */ protected $withCase = [ 'true' => self::T_TRUE, 'false' => self::T_FALSE, 'null' => self::T_NULL, ]; /** * Whether the next token starts immediately, or if there were * non-captured symbols before that */ public function nextTokenIsAdjacent(): bool { return $this->token === null || ($this->lookahead !== null && ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value'])); } /** * {@inheritdoc} */ protected function getCatchablePatterns() { return [ '[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*', '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', '"(?:""|[^"])*+"', ]; } /** * {@inheritdoc} */ protected function getNonCatchablePatterns() { return ['\s+', '\*+', '(.)']; } /** * {@inheritdoc} */ protected function getType(&$value) { $type = self::T_NONE; if ($value[0] === '"') { $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2)); return self::T_STRING; } if (isset($this->noCase[$value])) { return $this->noCase[$value]; } if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) { return self::T_IDENTIFIER; } $lowerValue = strtolower($value); if (isset($this->withCase[$lowerValue])) { return $this->withCase[$lowerValue]; } // Checking numeric value if (is_numeric($value)) { return strpos($value, '.') !== false || stripos($value, 'e') !== false ? self::T_FLOAT : self::T_INTEGER; } return $type; } /** @return array{value: int|string, type:self::T_*|null, position:int} */ public function peek(): ?array { $token = parent::peek(); if ($token === null) { return null; } return (array) $token; } } Doctrine/Common/Annotations/DocParser.php 0000644 00000141624 15120334141 0014435 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use Doctrine\Common\Annotations\Annotation\Attribute; use Doctrine\Common\Annotations\Annotation\Attributes; use Doctrine\Common\Annotations\Annotation\Enum; use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; use Doctrine\Common\Annotations\Annotation\Target; use ReflectionClass; use ReflectionException; use ReflectionProperty; use RuntimeException; use stdClass; use Throwable; use function array_keys; use function array_map; use function array_pop; use function array_values; use function class_exists; use function constant; use function count; use function defined; use function explode; use function gettype; use function implode; use function in_array; use function interface_exists; use function is_array; use function is_object; use function json_encode; use function ltrim; use function preg_match; use function reset; use function rtrim; use function sprintf; use function stripos; use function strlen; use function strpos; use function strrpos; use function strtolower; use function substr; use function trim; use const PHP_VERSION_ID; /** * A parser for docblock annotations. * * It is strongly discouraged to change the default annotation parsing process. */ final class DocParser { /** * An array of all valid tokens for a class name. * * @phpstan-var list<int> */ private static $classIdentifiers = [ DocLexer::T_IDENTIFIER, DocLexer::T_TRUE, DocLexer::T_FALSE, DocLexer::T_NULL, ]; /** * The lexer. * * @var DocLexer */ private $lexer; /** * Current target context. * * @var int */ private $target; /** * Doc parser used to collect annotation target. * * @var DocParser */ private static $metadataParser; /** * Flag to control if the current annotation is nested or not. * * @var bool */ private $isNestedAnnotation = false; /** * Hashmap containing all use-statements that are to be used when parsing * the given doc block. * * @var array<string, class-string> */ private $imports = []; /** * This hashmap is used internally to cache results of class_exists() * look-ups. * * @var array<class-string, bool> */ private $classExists = []; /** * Whether annotations that have not been imported should be ignored. * * @var bool */ private $ignoreNotImportedAnnotations = false; /** * An array of default namespaces if operating in simple mode. * * @var string[] */ private $namespaces = []; /** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names must be the raw names as used in the class, not the fully qualified * * @var bool[] indexed by annotation name */ private $ignoredAnnotationNames = []; /** * A list with annotations in namespaced format * that are not causing exceptions when not resolved to an annotation class. * * @var bool[] indexed by namespace name */ private $ignoredAnnotationNamespaces = []; /** @var string */ private $context = ''; /** * Hash-map for caching annotation metadata. * * @var array<class-string, mixed[]> */ private static $annotationMetadata = [ Annotation\Target::class => [ 'is_annotation' => true, 'has_constructor' => true, 'has_named_argument_constructor' => false, 'properties' => [], 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => Target::TARGET_CLASS, 'default_property' => 'value', 'attribute_types' => [ 'value' => [ 'required' => false, 'type' => 'array', 'array_type' => 'string', 'value' => 'array<string>', ], ], ], Annotation\Attribute::class => [ 'is_annotation' => true, 'has_constructor' => false, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_ANNOTATION', 'targets' => Target::TARGET_ANNOTATION, 'default_property' => 'name', 'properties' => [ 'name' => 'name', 'type' => 'type', 'required' => 'required', ], 'attribute_types' => [ 'value' => [ 'required' => true, 'type' => 'string', 'value' => 'string', ], 'type' => [ 'required' => true, 'type' => 'string', 'value' => 'string', ], 'required' => [ 'required' => false, 'type' => 'boolean', 'value' => 'boolean', ], ], ], Annotation\Attributes::class => [ 'is_annotation' => true, 'has_constructor' => false, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => Target::TARGET_CLASS, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => [ 'value' => [ 'type' => 'array', 'required' => true, 'array_type' => Annotation\Attribute::class, 'value' => 'array<' . Annotation\Attribute::class . '>', ], ], ], Annotation\Enum::class => [ 'is_annotation' => true, 'has_constructor' => true, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_PROPERTY', 'targets' => Target::TARGET_PROPERTY, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => [ 'value' => [ 'type' => 'array', 'required' => true, ], 'literal' => [ 'type' => 'array', 'required' => false, ], ], ], Annotation\NamedArgumentConstructor::class => [ 'is_annotation' => true, 'has_constructor' => false, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => Target::TARGET_CLASS, 'default_property' => null, 'properties' => [], 'attribute_types' => [], ], ]; /** * Hash-map for handle types declaration. * * @var array<string, string> */ private static $typeMap = [ 'float' => 'double', 'bool' => 'boolean', // allow uppercase Boolean in honor of George Boole 'Boolean' => 'boolean', 'int' => 'integer', ]; /** * Constructs a new DocParser. */ public function __construct() { $this->lexer = new DocLexer(); } /** * Sets the annotation names that are ignored during the parsing process. * * The names are supposed to be the raw names as used in the class, not the * fully qualified class names. * * @param bool[] $names indexed by annotation name * * @return void */ public function setIgnoredAnnotationNames(array $names) { $this->ignoredAnnotationNames = $names; } /** * Sets the annotation namespaces that are ignored during the parsing process. * * @param bool[] $ignoredAnnotationNamespaces indexed by annotation namespace name * * @return void */ public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces) { $this->ignoredAnnotationNamespaces = $ignoredAnnotationNamespaces; } /** * Sets ignore on not-imported annotations. * * @param bool $bool * * @return void */ public function setIgnoreNotImportedAnnotations($bool) { $this->ignoreNotImportedAnnotations = (bool) $bool; } /** * Sets the default namespaces. * * @param string $namespace * * @return void * * @throws RuntimeException */ public function addNamespace($namespace) { if ($this->imports) { throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); } $this->namespaces[] = $namespace; } /** * Sets the imports. * * @param array<string, class-string> $imports * * @return void * * @throws RuntimeException */ public function setImports(array $imports) { if ($this->namespaces) { throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); } $this->imports = $imports; } /** * Sets current target context as bitmask. * * @param int $target * * @return void */ public function setTarget($target) { $this->target = $target; } /** * Parses the given docblock string for annotations. * * @param string $input The docblock string to parse. * @param string $context The parsing context. * * @phpstan-return list<object> Array of annotations. If no annotations are found, an empty array is returned. * * @throws AnnotationException * @throws ReflectionException */ public function parse($input, $context = '') { $pos = $this->findInitialTokenPosition($input); if ($pos === null) { return []; } $this->context = $context; $this->lexer->setInput(trim(substr($input, $pos), '* /')); $this->lexer->moveNext(); return $this->Annotations(); } /** * Finds the first valid annotation * * @param string $input The docblock string to parse */ private function findInitialTokenPosition($input): ?int { $pos = 0; // search for first valid annotation while (($pos = strpos($input, '@', $pos)) !== false) { $preceding = substr($input, $pos - 1, 1); // if the @ is preceded by a space, a tab or * it is valid if ($pos === 0 || $preceding === ' ' || $preceding === '*' || $preceding === "\t") { return $pos; } $pos++; } return null; } /** * Attempts to match the given token with the current lookahead token. * If they match, updates the lookahead token; otherwise raises a syntax error. * * @param int $token Type of token. * * @return bool True if tokens match; false otherwise. * * @throws AnnotationException */ private function match(int $token): bool { if (! $this->lexer->isNextToken($token)) { throw $this->syntaxError($this->lexer->getLiteral($token)); } return $this->lexer->moveNext(); } /** * Attempts to match the current lookahead token with any of the given tokens. * * If any of them matches, this method updates the lookahead token; otherwise * a syntax error is raised. * * @phpstan-param list<mixed[]> $tokens * * @throws AnnotationException */ private function matchAny(array $tokens): bool { if (! $this->lexer->isNextTokenAny($tokens)) { throw $this->syntaxError(implode(' or ', array_map([$this->lexer, 'getLiteral'], $tokens))); } return $this->lexer->moveNext(); } /** * Generates a new syntax error. * * @param string $expected Expected string. * @param mixed[]|null $token Optional token. */ private function syntaxError(string $expected, ?array $token = null): AnnotationException { if ($token === null) { $token = $this->lexer->lookahead; } $message = sprintf('Expected %s, got ', $expected); $message .= $this->lexer->lookahead === null ? 'end of string' : sprintf("'%s' at position %s", $token['value'], $token['position']); if (strlen($this->context)) { $message .= ' in ' . $this->context; } $message .= '.'; return AnnotationException::syntaxError($message); } /** * Attempts to check if a class exists or not. This never goes through the PHP autoloading mechanism * but uses the {@link AnnotationRegistry} to load classes. * * @param class-string $fqcn */ private function classExists(string $fqcn): bool { if (isset($this->classExists[$fqcn])) { return $this->classExists[$fqcn]; } // first check if the class already exists, maybe loaded through another AnnotationReader if (class_exists($fqcn, false)) { return $this->classExists[$fqcn] = true; } // final check, does this class exist? return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn); } /** * Collects parsing metadata for a given annotation class * * @param class-string $name The annotation name * * @throws AnnotationException * @throws ReflectionException */ private function collectAnnotationMetadata(string $name): void { if (self::$metadataParser === null) { self::$metadataParser = new self(); self::$metadataParser->setIgnoreNotImportedAnnotations(true); self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames); self::$metadataParser->setImports([ 'enum' => Enum::class, 'target' => Target::class, 'attribute' => Attribute::class, 'attributes' => Attributes::class, 'namedargumentconstructor' => NamedArgumentConstructor::class, ]); // Make sure that annotations from metadata are loaded class_exists(Enum::class); class_exists(Target::class); class_exists(Attribute::class); class_exists(Attributes::class); class_exists(NamedArgumentConstructor::class); } $class = new ReflectionClass($name); $docComment = $class->getDocComment(); // Sets default values for annotation metadata $constructor = $class->getConstructor(); $metadata = [ 'default_property' => null, 'has_constructor' => $constructor !== null && $constructor->getNumberOfParameters() > 0, 'constructor_args' => [], 'properties' => [], 'property_types' => [], 'attribute_types' => [], 'targets_literal' => null, 'targets' => Target::TARGET_ALL, 'is_annotation' => strpos($docComment, '@Annotation') !== false, ]; $metadata['has_named_argument_constructor'] = $metadata['has_constructor'] && $class->implementsInterface(NamedArgumentConstructorAnnotation::class); // verify that the class is really meant to be an annotation if ($metadata['is_annotation']) { self::$metadataParser->setTarget(Target::TARGET_CLASS); foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { if ($annotation instanceof Target) { $metadata['targets'] = $annotation->targets; $metadata['targets_literal'] = $annotation->literal; continue; } if ($annotation instanceof NamedArgumentConstructor) { $metadata['has_named_argument_constructor'] = $metadata['has_constructor']; if ($metadata['has_named_argument_constructor']) { // choose the first argument as the default property $metadata['default_property'] = $constructor->getParameters()[0]->getName(); } } if (! ($annotation instanceof Attributes)) { continue; } foreach ($annotation->value as $attribute) { $this->collectAttributeTypeMetadata($metadata, $attribute); } } // if not has a constructor will inject values into public properties if ($metadata['has_constructor'] === false) { // collect all public properties foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { $metadata['properties'][$property->name] = $property->name; $propertyComment = $property->getDocComment(); if ($propertyComment === false) { continue; } $attribute = new Attribute(); $attribute->required = (strpos($propertyComment, '@Required') !== false); $attribute->name = $property->name; $attribute->type = (strpos($propertyComment, '@var') !== false && preg_match('/@var\s+([^\s]+)/', $propertyComment, $matches)) ? $matches[1] : 'mixed'; $this->collectAttributeTypeMetadata($metadata, $attribute); // checks if the property has @Enum if (strpos($propertyComment, '@Enum') === false) { continue; } $context = 'property ' . $class->name . '::$' . $property->name; self::$metadataParser->setTarget(Target::TARGET_PROPERTY); foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) { if (! $annotation instanceof Enum) { continue; } $metadata['enum'][$property->name]['value'] = $annotation->value; $metadata['enum'][$property->name]['literal'] = (! empty($annotation->literal)) ? $annotation->literal : $annotation->value; } } // choose the first property as default property $metadata['default_property'] = reset($metadata['properties']); } elseif ($metadata['has_named_argument_constructor']) { foreach ($constructor->getParameters() as $parameter) { if ($parameter->isVariadic()) { break; } $metadata['constructor_args'][$parameter->getName()] = [ 'position' => $parameter->getPosition(), 'default' => $parameter->isOptional() ? $parameter->getDefaultValue() : null, ]; } } } self::$annotationMetadata[$name] = $metadata; } /** * Collects parsing metadata for a given attribute. * * @param mixed[] $metadata */ private function collectAttributeTypeMetadata(array &$metadata, Attribute $attribute): void { // handle internal type declaration $type = self::$typeMap[$attribute->type] ?? $attribute->type; // handle the case if the property type is mixed if ($type === 'mixed') { return; } // Evaluate type $pos = strpos($type, '<'); if ($pos !== false) { // Checks if the property has array<type> $arrayType = substr($type, $pos + 1, -1); $type = 'array'; if (isset(self::$typeMap[$arrayType])) { $arrayType = self::$typeMap[$arrayType]; } $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; } else { // Checks if the property has type[] $pos = strrpos($type, '['); if ($pos !== false) { $arrayType = substr($type, 0, $pos); $type = 'array'; if (isset(self::$typeMap[$arrayType])) { $arrayType = self::$typeMap[$arrayType]; } $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; } } $metadata['attribute_types'][$attribute->name]['type'] = $type; $metadata['attribute_types'][$attribute->name]['value'] = $attribute->type; $metadata['attribute_types'][$attribute->name]['required'] = $attribute->required; } /** * Annotations ::= Annotation {[ "*" ]* [Annotation]}* * * @phpstan-return list<object> * * @throws AnnotationException * @throws ReflectionException */ private function Annotations(): array { $annotations = []; while ($this->lexer->lookahead !== null) { if ($this->lexer->lookahead['type'] !== DocLexer::T_AT) { $this->lexer->moveNext(); continue; } // make sure the @ is preceded by non-catchable pattern if ( $this->lexer->token !== null && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen( $this->lexer->token['value'] ) ) { $this->lexer->moveNext(); continue; } // make sure the @ is followed by either a namespace separator, or // an identifier token $peek = $this->lexer->glimpse(); if ( ($peek === null) || ($peek['type'] !== DocLexer::T_NAMESPACE_SEPARATOR && ! in_array( $peek['type'], self::$classIdentifiers, true )) || $peek['position'] !== $this->lexer->lookahead['position'] + 1 ) { $this->lexer->moveNext(); continue; } $this->isNestedAnnotation = false; $annot = $this->Annotation(); if ($annot === false) { continue; } $annotations[] = $annot; } return $annotations; } /** * Annotation ::= "@" AnnotationName MethodCall * AnnotationName ::= QualifiedName | SimpleName * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName * NameSpacePart ::= identifier | null | false | true * SimpleName ::= identifier | null | false | true * * @return object|false False if it is not a valid annotation. * * @throws AnnotationException * @throws ReflectionException */ private function Annotation() { $this->match(DocLexer::T_AT); // check if we have an annotation $name = $this->Identifier(); if ( $this->lexer->isNextToken(DocLexer::T_MINUS) && $this->lexer->nextTokenIsAdjacent() ) { // Annotations with dashes, such as "@foo-" or "@foo-bar", are to be discarded return false; } // only process names which are not fully qualified, yet // fully qualified names must start with a \ $originalName = $name; if ($name[0] !== '\\') { $pos = strpos($name, '\\'); $alias = ($pos === false) ? $name : substr($name, 0, $pos); $found = false; $loweredAlias = strtolower($alias); if ($this->namespaces) { foreach ($this->namespaces as $namespace) { if ($this->classExists($namespace . '\\' . $name)) { $name = $namespace . '\\' . $name; $found = true; break; } } } elseif (isset($this->imports[$loweredAlias])) { $namespace = ltrim($this->imports[$loweredAlias], '\\'); $name = ($pos !== false) ? $namespace . substr($name, $pos) : $namespace; $found = $this->classExists($name); } elseif ( ! isset($this->ignoredAnnotationNames[$name]) && isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name) ) { $name = $this->imports['__NAMESPACE__'] . '\\' . $name; $found = true; } elseif (! isset($this->ignoredAnnotationNames[$name]) && $this->classExists($name)) { $found = true; } if (! $found) { if ($this->isIgnoredAnnotation($name)) { return false; } throw AnnotationException::semanticalError(sprintf( <<<'EXCEPTION' The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation? EXCEPTION , $name, $this->context )); } } $name = ltrim($name, '\\'); if (! $this->classExists($name)) { throw AnnotationException::semanticalError(sprintf( 'The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context )); } // at this point, $name contains the fully qualified class name of the // annotation, and it is also guaranteed that this class exists, and // that it is loaded // collects the metadata annotation only if there is not yet if (! isset(self::$annotationMetadata[$name])) { $this->collectAnnotationMetadata($name); } // verify that the class is really meant to be an annotation and not just any ordinary class if (self::$annotationMetadata[$name]['is_annotation'] === false) { if ($this->isIgnoredAnnotation($originalName) || $this->isIgnoredAnnotation($name)) { return false; } throw AnnotationException::semanticalError(sprintf( <<<'EXCEPTION' The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s. EXCEPTION , $name, $name, $originalName, $this->context )); } //if target is nested annotation $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target; // Next will be nested $this->isNestedAnnotation = true; //if annotation does not support current target if ((self::$annotationMetadata[$name]['targets'] & $target) === 0 && $target) { throw AnnotationException::semanticalError( sprintf( <<<'EXCEPTION' Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s. EXCEPTION , $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal'] ) ); } $arguments = $this->MethodCall(); $values = $this->resolvePositionalValues($arguments, $name); if (isset(self::$annotationMetadata[$name]['enum'])) { // checks all declared attributes foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) { // checks if the attribute is a valid enumerator if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) { throw AnnotationException::enumeratorError( $property, $name, $this->context, $enum['literal'], $values[$property] ); } } } // checks all declared attributes foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) { if ( $property === self::$annotationMetadata[$name]['default_property'] && ! isset($values[$property]) && isset($values['value']) ) { $property = 'value'; } // handle a not given attribute or null value if (! isset($values[$property])) { if ($type['required']) { throw AnnotationException::requiredError( $property, $originalName, $this->context, 'a(n) ' . $type['value'] ); } continue; } if ($type['type'] === 'array') { // handle the case of a single value if (! is_array($values[$property])) { $values[$property] = [$values[$property]]; } // checks if the attribute has array type declaration, such as "array<string>" if (isset($type['array_type'])) { foreach ($values[$property] as $item) { if (gettype($item) !== $type['array_type'] && ! $item instanceof $type['array_type']) { throw AnnotationException::attributeTypeError( $property, $originalName, $this->context, 'either a(n) ' . $type['array_type'] . ', or an array of ' . $type['array_type'] . 's', $item ); } } } } elseif (gettype($values[$property]) !== $type['type'] && ! $values[$property] instanceof $type['type']) { throw AnnotationException::attributeTypeError( $property, $originalName, $this->context, 'a(n) ' . $type['value'], $values[$property] ); } } if (self::$annotationMetadata[$name]['has_named_argument_constructor']) { if (PHP_VERSION_ID >= 80000) { foreach ($values as $property => $value) { if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) { throw AnnotationException::creationError(sprintf( <<<'EXCEPTION' The annotation @%s declared on %s does not have a property named "%s" that can be set through its named arguments constructor. Available named arguments: %s EXCEPTION , $originalName, $this->context, $property, implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args'])) )); } } return $this->instantiateAnnotiation($originalName, $this->context, $name, $values); } $positionalValues = []; foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) { $positionalValues[$parameter['position']] = $parameter['default']; } foreach ($values as $property => $value) { if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) { throw AnnotationException::creationError(sprintf( <<<'EXCEPTION' The annotation @%s declared on %s does not have a property named "%s" that can be set through its named arguments constructor. Available named arguments: %s EXCEPTION , $originalName, $this->context, $property, implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args'])) )); } $positionalValues[self::$annotationMetadata[$name]['constructor_args'][$property]['position']] = $value; } return $this->instantiateAnnotiation($originalName, $this->context, $name, $positionalValues); } // check if the annotation expects values via the constructor, // or directly injected into public properties if (self::$annotationMetadata[$name]['has_constructor'] === true) { return $this->instantiateAnnotiation($originalName, $this->context, $name, [$values]); } $instance = $this->instantiateAnnotiation($originalName, $this->context, $name, []); foreach ($values as $property => $value) { if (! isset(self::$annotationMetadata[$name]['properties'][$property])) { if ($property !== 'value') { throw AnnotationException::creationError(sprintf( <<<'EXCEPTION' The annotation @%s declared on %s does not have a property named "%s". Available properties: %s EXCEPTION , $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties']) )); } // handle the case if the property has no annotations $property = self::$annotationMetadata[$name]['default_property']; if (! $property) { throw AnnotationException::creationError(sprintf( 'The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values) )); } } $instance->{$property} = $value; } return $instance; } /** * MethodCall ::= ["(" [Values] ")"] * * @return mixed[] * * @throws AnnotationException * @throws ReflectionException */ private function MethodCall(): array { $values = []; if (! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { return $values; } $this->match(DocLexer::T_OPEN_PARENTHESIS); if (! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { $values = $this->Values(); } $this->match(DocLexer::T_CLOSE_PARENTHESIS); return $values; } /** * Values ::= Array | Value {"," Value}* [","] * * @return mixed[] * * @throws AnnotationException * @throws ReflectionException */ private function Values(): array { $values = [$this->Value()]; while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { $this->match(DocLexer::T_COMMA); if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { break; } $token = $this->lexer->lookahead; $value = $this->Value(); $values[] = $value; } $namedArguments = []; $positionalArguments = []; foreach ($values as $k => $value) { if (is_object($value) && $value instanceof stdClass) { $namedArguments[$value->name] = $value->value; } else { $positionalArguments[$k] = $value; } } return ['named_arguments' => $namedArguments, 'positional_arguments' => $positionalArguments]; } /** * Constant ::= integer | string | float | boolean * * @return mixed * * @throws AnnotationException */ private function Constant() { $identifier = $this->Identifier(); if (! defined($identifier) && strpos($identifier, '::') !== false && $identifier[0] !== '\\') { [$className, $const] = explode('::', $identifier); $pos = strpos($className, '\\'); $alias = ($pos === false) ? $className : substr($className, 0, $pos); $found = false; $loweredAlias = strtolower($alias); switch (true) { case ! empty($this->namespaces): foreach ($this->namespaces as $ns) { if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) { $className = $ns . '\\' . $className; $found = true; break; } } break; case isset($this->imports[$loweredAlias]): $found = true; $className = ($pos !== false) ? $this->imports[$loweredAlias] . substr($className, $pos) : $this->imports[$loweredAlias]; break; default: if (isset($this->imports['__NAMESPACE__'])) { $ns = $this->imports['__NAMESPACE__']; if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) { $className = $ns . '\\' . $className; $found = true; } } break; } if ($found) { $identifier = $className . '::' . $const; } } /** * Checks if identifier ends with ::class and remove the leading backslash if it exists. */ if ( $this->identifierEndsWithClassConstant($identifier) && ! $this->identifierStartsWithBackslash($identifier) ) { return substr($identifier, 0, $this->getClassConstantPositionInIdentifier($identifier)); } if ($this->identifierEndsWithClassConstant($identifier) && $this->identifierStartsWithBackslash($identifier)) { return substr($identifier, 1, $this->getClassConstantPositionInIdentifier($identifier) - 1); } if (! defined($identifier)) { throw AnnotationException::semanticalErrorConstants($identifier, $this->context); } return constant($identifier); } private function identifierStartsWithBackslash(string $identifier): bool { return $identifier[0] === '\\'; } private function identifierEndsWithClassConstant(string $identifier): bool { return $this->getClassConstantPositionInIdentifier($identifier) === strlen($identifier) - strlen('::class'); } /** @return int|false */ private function getClassConstantPositionInIdentifier(string $identifier) { return stripos($identifier, '::class'); } /** * Identifier ::= string * * @throws AnnotationException */ private function Identifier(): string { // check if we have an annotation if (! $this->lexer->isNextTokenAny(self::$classIdentifiers)) { throw $this->syntaxError('namespace separator or identifier'); } $this->lexer->moveNext(); $className = $this->lexer->token['value']; while ( $this->lexer->lookahead !== null && $this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value'])) && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR) ) { $this->match(DocLexer::T_NAMESPACE_SEPARATOR); $this->matchAny(self::$classIdentifiers); $className .= '\\' . $this->lexer->token['value']; } return $className; } /** * Value ::= PlainValue | FieldAssignment * * @return mixed * * @throws AnnotationException * @throws ReflectionException */ private function Value() { $peek = $this->lexer->glimpse(); if ($peek['type'] === DocLexer::T_EQUALS) { return $this->FieldAssignment(); } return $this->PlainValue(); } /** * PlainValue ::= integer | string | float | boolean | Array | Annotation * * @return mixed * * @throws AnnotationException * @throws ReflectionException */ private function PlainValue() { if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { return $this->Arrayx(); } if ($this->lexer->isNextToken(DocLexer::T_AT)) { return $this->Annotation(); } if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { return $this->Constant(); } switch ($this->lexer->lookahead['type']) { case DocLexer::T_STRING: $this->match(DocLexer::T_STRING); return $this->lexer->token['value']; case DocLexer::T_INTEGER: $this->match(DocLexer::T_INTEGER); return (int) $this->lexer->token['value']; case DocLexer::T_FLOAT: $this->match(DocLexer::T_FLOAT); return (float) $this->lexer->token['value']; case DocLexer::T_TRUE: $this->match(DocLexer::T_TRUE); return true; case DocLexer::T_FALSE: $this->match(DocLexer::T_FALSE); return false; case DocLexer::T_NULL: $this->match(DocLexer::T_NULL); return null; default: throw $this->syntaxError('PlainValue'); } } /** * FieldAssignment ::= FieldName "=" PlainValue * FieldName ::= identifier * * @throws AnnotationException * @throws ReflectionException */ private function FieldAssignment(): stdClass { $this->match(DocLexer::T_IDENTIFIER); $fieldName = $this->lexer->token['value']; $this->match(DocLexer::T_EQUALS); $item = new stdClass(); $item->name = $fieldName; $item->value = $this->PlainValue(); return $item; } /** * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}" * * @return mixed[] * * @throws AnnotationException * @throws ReflectionException */ private function Arrayx(): array { $array = $values = []; $this->match(DocLexer::T_OPEN_CURLY_BRACES); // If the array is empty, stop parsing and return. if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { $this->match(DocLexer::T_CLOSE_CURLY_BRACES); return $array; } $values[] = $this->ArrayEntry(); while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { $this->match(DocLexer::T_COMMA); // optional trailing comma if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { break; } $values[] = $this->ArrayEntry(); } $this->match(DocLexer::T_CLOSE_CURLY_BRACES); foreach ($values as $value) { [$key, $val] = $value; if ($key !== null) { $array[$key] = $val; } else { $array[] = $val; } } return $array; } /** * ArrayEntry ::= Value | KeyValuePair * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant * Key ::= string | integer | Constant * * @phpstan-return array{mixed, mixed} * * @throws AnnotationException * @throws ReflectionException */ private function ArrayEntry(): array { $peek = $this->lexer->glimpse(); if ( $peek['type'] === DocLexer::T_EQUALS || $peek['type'] === DocLexer::T_COLON ) { if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { $key = $this->Constant(); } else { $this->matchAny([DocLexer::T_INTEGER, DocLexer::T_STRING]); $key = $this->lexer->token['value']; } $this->matchAny([DocLexer::T_EQUALS, DocLexer::T_COLON]); return [$key, $this->PlainValue()]; } return [null, $this->Value()]; } /** * Checks whether the given $name matches any ignored annotation name or namespace */ private function isIgnoredAnnotation(string $name): bool { if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) { return true; } foreach (array_keys($this->ignoredAnnotationNamespaces) as $ignoredAnnotationNamespace) { $ignoredAnnotationNamespace = rtrim($ignoredAnnotationNamespace, '\\') . '\\'; if (stripos(rtrim($name, '\\') . '\\', $ignoredAnnotationNamespace) === 0) { return true; } } return false; } /** * Resolve positional arguments (without name) to named ones * * @param array<string,mixed> $arguments * * @return array<string,mixed> */ private function resolvePositionalValues(array $arguments, string $name): array { $positionalArguments = $arguments['positional_arguments'] ?? []; $values = $arguments['named_arguments'] ?? []; if ( self::$annotationMetadata[$name]['has_named_argument_constructor'] && self::$annotationMetadata[$name]['default_property'] !== null ) { // We must ensure that we don't have positional arguments after named ones $positions = array_keys($positionalArguments); $lastPosition = null; foreach ($positions as $position) { if ( ($lastPosition === null && $position !== 0) || ($lastPosition !== null && $position !== $lastPosition + 1) ) { throw $this->syntaxError('Positional arguments after named arguments is not allowed'); } $lastPosition = $position; } foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) { $position = $parameter['position']; if (isset($values[$property]) || ! isset($positionalArguments[$position])) { continue; } $values[$property] = $positionalArguments[$position]; } } else { if (count($positionalArguments) > 0 && ! isset($values['value'])) { if (count($positionalArguments) === 1) { $value = array_pop($positionalArguments); } else { $value = array_values($positionalArguments); } $values['value'] = $value; } } return $values; } /** * Try to instantiate the annotation and catch and process any exceptions related to failure * * @param class-string $name * @param array<string,mixed> $arguments * * @return object * * @throws AnnotationException */ private function instantiateAnnotiation(string $originalName, string $context, string $name, array $arguments) { try { return new $name(...$arguments); } catch (Throwable $exception) { throw AnnotationException::creationError( sprintf( 'An error occurred while instantiating the annotation @%s declared on %s: "%s".', $originalName, $context, $exception->getMessage() ), $exception ); } } } Doctrine/Common/Annotations/FileCacheReader.php 0000644 00000020626 15120334141 0015477 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use InvalidArgumentException; use ReflectionClass; use ReflectionMethod; use ReflectionProperty; use RuntimeException; use function chmod; use function file_put_contents; use function filemtime; use function gettype; use function is_dir; use function is_file; use function is_int; use function is_writable; use function mkdir; use function rename; use function rtrim; use function serialize; use function sha1; use function sprintf; use function strtr; use function tempnam; use function uniqid; use function unlink; use function var_export; /** * File cache reader for annotations. * * @deprecated the FileCacheReader is deprecated and will be removed * in version 2.0.0 of doctrine/annotations. Please use the * {@see \Doctrine\Common\Annotations\PsrCachedReader} instead. */ class FileCacheReader implements Reader { /** @var Reader */ private $reader; /** @var string */ private $dir; /** @var bool */ private $debug; /** @phpstan-var array<string, list<object>> */ private $loadedAnnotations = []; /** @var array<string, string> */ private $classNameHashes = []; /** @var int */ private $umask; /** * @param string $cacheDir * @param bool $debug * @param int $umask * * @throws InvalidArgumentException */ public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002) { if (! is_int($umask)) { throw new InvalidArgumentException(sprintf( 'The parameter umask must be an integer, was: %s', gettype($umask) )); } $this->reader = $reader; $this->umask = $umask; if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) { throw new InvalidArgumentException(sprintf( 'The directory "%s" does not exist and could not be created.', $cacheDir )); } $this->dir = rtrim($cacheDir, '\\/'); $this->debug = $debug; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { if (! isset($this->classNameHashes[$class->name])) { $this->classNameHashes[$class->name] = sha1($class->name); } $key = $this->classNameHashes[$class->name]; if (isset($this->loadedAnnotations[$key])) { return $this->loadedAnnotations[$key]; } $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; if (! is_file($path)) { $annot = $this->reader->getClassAnnotations($class); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } $filename = $class->getFilename(); if ( $this->debug && $filename !== false && filemtime($path) < filemtime($filename) ) { @unlink($path); $annot = $this->reader->getClassAnnotations($class); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } return $this->loadedAnnotations[$key] = include $path; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); if (! isset($this->classNameHashes[$class->name])) { $this->classNameHashes[$class->name] = sha1($class->name); } $key = $this->classNameHashes[$class->name] . '$' . $property->getName(); if (isset($this->loadedAnnotations[$key])) { return $this->loadedAnnotations[$key]; } $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; if (! is_file($path)) { $annot = $this->reader->getPropertyAnnotations($property); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } $filename = $class->getFilename(); if ( $this->debug && $filename !== false && filemtime($path) < filemtime($filename) ) { @unlink($path); $annot = $this->reader->getPropertyAnnotations($property); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } return $this->loadedAnnotations[$key] = include $path; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); if (! isset($this->classNameHashes[$class->name])) { $this->classNameHashes[$class->name] = sha1($class->name); } $key = $this->classNameHashes[$class->name] . '#' . $method->getName(); if (isset($this->loadedAnnotations[$key])) { return $this->loadedAnnotations[$key]; } $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; if (! is_file($path)) { $annot = $this->reader->getMethodAnnotations($method); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } $filename = $class->getFilename(); if ( $this->debug && $filename !== false && filemtime($path) < filemtime($filename) ) { @unlink($path); $annot = $this->reader->getMethodAnnotations($method); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } return $this->loadedAnnotations[$key] = include $path; } /** * Saves the cache file. * * @param string $path * @param mixed $data * * @return void */ private function saveCacheFile($path, $data) { if (! is_writable($this->dir)) { throw new InvalidArgumentException(sprintf( <<<'EXCEPTION' The directory "%s" is not writable. Both the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package., EXCEPTION , $this->dir )); } $tempfile = tempnam($this->dir, uniqid('', true)); if ($tempfile === false) { throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir)); } @chmod($tempfile, 0666 & (~$this->umask)); $written = file_put_contents( $tempfile, '<?php return unserialize(' . var_export(serialize($data), true) . ');' ); if ($written === false) { throw new RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile)); } @chmod($tempfile, 0666 & (~$this->umask)); if (rename($tempfile, $path) === false) { @unlink($tempfile); throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path)); } } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { $annotations = $this->getClassAnnotations($class); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { $annotations = $this->getMethodAnnotations($method); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { $annotations = $this->getPropertyAnnotations($property); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * Clears loaded annotations. * * @return void */ public function clearLoadedAnnotations() { $this->loadedAnnotations = []; } } Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php 0000644 00000012655 15120334141 0021222 0 ustar 00 <?php declare(strict_types=1); namespace Doctrine\Common\Annotations; /** * A list of annotations that are implicitly ignored during the parsing process. * * All names are case sensitive. */ final class ImplicitlyIgnoredAnnotationNames { private const Reserved = [ 'Annotation' => true, 'Attribute' => true, 'Attributes' => true, /* Can we enable this? 'Enum' => true, */ 'Required' => true, 'Target' => true, 'NamedArgumentConstructor' => true, ]; private const WidelyUsedNonStandard = [ 'fix' => true, 'fixme' => true, 'override' => true, ]; private const PhpDocumentor1 = [ 'abstract' => true, 'access' => true, 'code' => true, 'deprec' => true, 'endcode' => true, 'exception' => true, 'final' => true, 'ingroup' => true, 'inheritdoc' => true, 'inheritDoc' => true, 'magic' => true, 'name' => true, 'private' => true, 'static' => true, 'staticvar' => true, 'staticVar' => true, 'toc' => true, 'tutorial' => true, 'throw' => true, ]; private const PhpDocumentor2 = [ 'api' => true, 'author' => true, 'category' => true, 'copyright' => true, 'deprecated' => true, 'example' => true, 'filesource' => true, 'global' => true, 'ignore' => true, /* Can we enable this? 'index' => true, */ 'internal' => true, 'license' => true, 'link' => true, 'method' => true, 'package' => true, 'param' => true, 'property' => true, 'property-read' => true, 'property-write' => true, 'return' => true, 'see' => true, 'since' => true, 'source' => true, 'subpackage' => true, 'throws' => true, 'todo' => true, 'TODO' => true, 'usedby' => true, 'uses' => true, 'var' => true, 'version' => true, ]; private const PHPUnit = [ 'author' => true, 'after' => true, 'afterClass' => true, 'backupGlobals' => true, 'backupStaticAttributes' => true, 'before' => true, 'beforeClass' => true, 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, 'covers' => true, 'coversDefaultClass' => true, 'coversNothing' => true, 'dataProvider' => true, 'depends' => true, 'doesNotPerformAssertions' => true, 'expectedException' => true, 'expectedExceptionCode' => true, 'expectedExceptionMessage' => true, 'expectedExceptionMessageRegExp' => true, 'group' => true, 'large' => true, 'medium' => true, 'preserveGlobalState' => true, 'requires' => true, 'runTestsInSeparateProcesses' => true, 'runInSeparateProcess' => true, 'small' => true, 'test' => true, 'testdox' => true, 'testWith' => true, 'ticket' => true, 'uses' => true, ]; private const PhpCheckStyle = ['SuppressWarnings' => true]; private const PhpStorm = ['noinspection' => true]; private const PEAR = ['package_version' => true]; private const PlainUML = [ 'startuml' => true, 'enduml' => true, ]; private const Symfony = ['experimental' => true]; private const PhpCodeSniffer = [ 'codingStandardsIgnoreStart' => true, 'codingStandardsIgnoreEnd' => true, ]; private const SlevomatCodingStandard = ['phpcsSuppress' => true]; private const Phan = ['suppress' => true]; private const Rector = ['noRector' => true]; private const StaticAnalysis = [ // PHPStan, Psalm 'extends' => true, 'implements' => true, 'readonly' => true, 'template' => true, 'use' => true, // Psalm 'pure' => true, 'immutable' => true, ]; public const LIST = self::Reserved + self::WidelyUsedNonStandard + self::PhpDocumentor1 + self::PhpDocumentor2 + self::PHPUnit + self::PhpCheckStyle + self::PhpStorm + self::PEAR + self::PlainUML + self::Symfony + self::SlevomatCodingStandard + self::PhpCodeSniffer + self::Phan + self::Rector + self::StaticAnalysis; private function __construct() { } } Doctrine/Common/Annotations/IndexedReader.php 0000644 00000004376 15120334141 0015260 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use ReflectionClass; use ReflectionMethod; use ReflectionProperty; use function call_user_func_array; use function get_class; /** * Allows the reader to be used in-place of Doctrine's reader. */ class IndexedReader implements Reader { /** @var Reader */ private $delegate; public function __construct(Reader $reader) { $this->delegate = $reader; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $annotations = []; foreach ($this->delegate->getClassAnnotations($class) as $annot) { $annotations[get_class($annot)] = $annot; } return $annotations; } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { return $this->delegate->getClassAnnotation($class, $annotationName); } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $annotations = []; foreach ($this->delegate->getMethodAnnotations($method) as $annot) { $annotations[get_class($annot)] = $annot; } return $annotations; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { return $this->delegate->getMethodAnnotation($method, $annotationName); } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $annotations = []; foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { $annotations[get_class($annot)] = $annot; } return $annotations; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { return $this->delegate->getPropertyAnnotation($property, $annotationName); } /** * Proxies all methods to the delegate. * * @param string $method * @param mixed[] $args * * @return mixed */ public function __call($method, $args) { return call_user_func_array([$this->delegate, $method], $args); } } Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php 0000644 00000000532 15120334141 0021573 0 ustar 00 <?php namespace Doctrine\Common\Annotations; /** * Marker interface for PHP7/PHP8 compatible support * for named arguments (and constructor property promotion). * * @deprecated Implementing this interface is deprecated * Use the Annotation @NamedArgumentConstructor instead */ interface NamedArgumentConstructorAnnotation { } Doctrine/Common/Annotations/PhpParser.php 0000644 00000004661 15120334141 0014456 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use ReflectionClass; use ReflectionFunction; use SplFileObject; use function is_file; use function method_exists; use function preg_quote; use function preg_replace; /** * Parses a file for namespaces/use/class declarations. */ final class PhpParser { /** * Parses a class. * * @deprecated use parseUseStatements instead * * @param ReflectionClass $class A <code>ReflectionClass</code> object. * * @return array<string, class-string> A list with use statements in the form (Alias => FQN). */ public function parseClass(ReflectionClass $class) { return $this->parseUseStatements($class); } /** * Parse a class or function for use statements. * * @param ReflectionClass|ReflectionFunction $reflection * * @psalm-return array<string, string> a list with use statements in the form (Alias => FQN). */ public function parseUseStatements($reflection): array { if (method_exists($reflection, 'getUseStatements')) { return $reflection->getUseStatements(); } $filename = $reflection->getFileName(); if ($filename === false) { return []; } $content = $this->getFileContent($filename, $reflection->getStartLine()); if ($content === null) { return []; } $namespace = preg_quote($reflection->getNamespaceName()); $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content); $tokenizer = new TokenParser('<?php ' . $content); return $tokenizer->parseUseStatements($reflection->getNamespaceName()); } /** * Gets the content of the file right up to the given line number. * * @param string $filename The name of the file to load. * @param int $lineNumber The number of lines to read from file. * * @return string|null The content of the file or null if the file does not exist. */ private function getFileContent($filename, $lineNumber) { if (! is_file($filename)) { return null; } $content = ''; $lineCnt = 0; $file = new SplFileObject($filename); while (! $file->eof()) { if ($lineCnt++ === $lineNumber) { break; } $content .= $file->fgets(); } return $content; } } Doctrine/Common/Annotations/PsrCachedReader.php 0000644 00000014525 15120334141 0015531 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use Psr\Cache\CacheItemPoolInterface; use ReflectionClass; use ReflectionMethod; use ReflectionProperty; use Reflector; use function array_map; use function array_merge; use function assert; use function filemtime; use function max; use function rawurlencode; use function time; /** * A cache aware annotation reader. */ final class PsrCachedReader implements Reader { /** @var Reader */ private $delegate; /** @var CacheItemPoolInterface */ private $cache; /** @var bool */ private $debug; /** @var array<string, array<object>> */ private $loadedAnnotations = []; /** @var int[] */ private $loadedFilemtimes = []; public function __construct(Reader $reader, CacheItemPoolInterface $cache, bool $debug = false) { $this->delegate = $reader; $this->cache = $cache; $this->debug = (bool) $debug; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $cacheKey = $class->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class, 'getClassAnnotations', $class); return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { foreach ($this->getClassAnnotations($class) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $cacheKey = $class->getName() . '$' . $property->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class, 'getPropertyAnnotations', $property); return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { foreach ($this->getPropertyAnnotations($property) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $cacheKey = $class->getName() . '#' . $method->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class, 'getMethodAnnotations', $method); return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { foreach ($this->getMethodAnnotations($method) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } public function clearLoadedAnnotations(): void { $this->loadedAnnotations = []; $this->loadedFilemtimes = []; } /** @return mixed[] */ private function fetchFromCache( string $cacheKey, ReflectionClass $class, string $method, Reflector $reflector ): array { $cacheKey = rawurlencode($cacheKey); $item = $this->cache->getItem($cacheKey); if (($this->debug && ! $this->refresh($cacheKey, $class)) || ! $item->isHit()) { $this->cache->save($item->set($this->delegate->{$method}($reflector))); } return $item->get(); } /** * Used in debug mode to check if the cache is fresh. * * @return bool Returns true if the cache was fresh, or false if the class * being read was modified since writing to the cache. */ private function refresh(string $cacheKey, ReflectionClass $class): bool { $lastModification = $this->getLastModification($class); if ($lastModification === 0) { return true; } $item = $this->cache->getItem('[C]' . $cacheKey); if ($item->isHit() && $item->get() >= $lastModification) { return true; } $this->cache->save($item->set(time())); return false; } /** * Returns the time the class was last modified, testing traits and parents */ private function getLastModification(ReflectionClass $class): int { $filename = $class->getFileName(); if (isset($this->loadedFilemtimes[$filename])) { return $this->loadedFilemtimes[$filename]; } $parent = $class->getParentClass(); $lastModification = max(array_merge( [$filename ? filemtime($filename) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $class->getTraits()), array_map(function (ReflectionClass $class): int { return $this->getLastModification($class); }, $class->getInterfaces()), $parent ? [$this->getLastModification($parent)] : [] )); assert($lastModification !== false); return $this->loadedFilemtimes[$filename] = $lastModification; } private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int { $fileName = $reflectionTrait->getFileName(); if (isset($this->loadedFilemtimes[$fileName])) { return $this->loadedFilemtimes[$fileName]; } $lastModificationTime = max(array_merge( [$fileName ? filemtime($fileName) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $reflectionTrait->getTraits()) )); assert($lastModificationTime !== false); return $this->loadedFilemtimes[$fileName] = $lastModificationTime; } } Doctrine/Common/Annotations/Reader.php 0000644 00000004752 15120334141 0013755 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use ReflectionClass; use ReflectionMethod; use ReflectionProperty; /** * Interface for annotation readers. */ interface Reader { /** * Gets the annotations applied to a class. * * @param ReflectionClass $class The ReflectionClass of the class from which * the class annotations should be read. * * @return array<object> An array of Annotations. */ public function getClassAnnotations(ReflectionClass $class); /** * Gets a class annotation. * * @param ReflectionClass $class The ReflectionClass of the class from which * the class annotations should be read. * @param class-string<T> $annotationName The name of the annotation. * * @return T|null The Annotation or NULL, if the requested annotation does not exist. * * @template T */ public function getClassAnnotation(ReflectionClass $class, $annotationName); /** * Gets the annotations applied to a method. * * @param ReflectionMethod $method The ReflectionMethod of the method from which * the annotations should be read. * * @return array<object> An array of Annotations. */ public function getMethodAnnotations(ReflectionMethod $method); /** * Gets a method annotation. * * @param ReflectionMethod $method The ReflectionMethod to read the annotations from. * @param class-string<T> $annotationName The name of the annotation. * * @return T|null The Annotation or NULL, if the requested annotation does not exist. * * @template T */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName); /** * Gets the annotations applied to a property. * * @param ReflectionProperty $property The ReflectionProperty of the property * from which the annotations should be read. * * @return array<object> An array of Annotations. */ public function getPropertyAnnotations(ReflectionProperty $property); /** * Gets a property annotation. * * @param ReflectionProperty $property The ReflectionProperty to read the annotations from. * @param class-string<T> $annotationName The name of the annotation. * * @return T|null The Annotation or NULL, if the requested annotation does not exist. * * @template T */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName); } Doctrine/Common/Annotations/SimpleAnnotationReader.php 0000644 00000005241 15120334141 0017154 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use ReflectionClass; use ReflectionMethod; use ReflectionProperty; /** * Simple Annotation Reader. * * This annotation reader is intended to be used in projects where you have * full-control over all annotations that are available. * * @deprecated Deprecated in favour of using AnnotationReader */ class SimpleAnnotationReader implements Reader { /** @var DocParser */ private $parser; /** * Initializes a new SimpleAnnotationReader. */ public function __construct() { $this->parser = new DocParser(); $this->parser->setIgnoreNotImportedAnnotations(true); } /** * Adds a namespace in which we will look for annotations. * * @param string $namespace * * @return void */ public function addNamespace($namespace) { $this->parser->addNamespace($namespace); } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { return $this->parser->parse( $method->getDocComment(), 'method ' . $method->getDeclaringClass()->name . '::' . $method->getName() . '()' ); } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { return $this->parser->parse( $property->getDocComment(), 'property ' . $property->getDeclaringClass()->name . '::$' . $property->getName() ); } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { foreach ($this->getClassAnnotations($class) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { foreach ($this->getMethodAnnotations($method) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { foreach ($this->getPropertyAnnotations($property) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } } Doctrine/Common/Annotations/TokenParser.php 0000644 00000014244 15120334141 0015005 0 ustar 00 <?php namespace Doctrine\Common\Annotations; use function array_merge; use function count; use function explode; use function strtolower; use function token_get_all; use const PHP_VERSION_ID; use const T_AS; use const T_COMMENT; use const T_DOC_COMMENT; use const T_NAME_FULLY_QUALIFIED; use const T_NAME_QUALIFIED; use const T_NAMESPACE; use const T_NS_SEPARATOR; use const T_STRING; use const T_USE; use const T_WHITESPACE; /** * Parses a file for namespaces/use/class declarations. */ class TokenParser { /** * The token list. * * @phpstan-var list<mixed[]> */ private $tokens; /** * The number of tokens. * * @var int */ private $numTokens; /** * The current array pointer. * * @var int */ private $pointer = 0; /** @param string $contents */ public function __construct($contents) { $this->tokens = token_get_all($contents); // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a // docblock. If the first thing in the file is a class without a doc block this would cause calls to // getDocBlock() on said class to return our long lost doc_comment. Argh. // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least // it's harmless to us. token_get_all("<?php\n/**\n *\n */"); $this->numTokens = count($this->tokens); } /** * Gets the next non whitespace and non comment token. * * @param bool $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped. * If FALSE then only whitespace and normal comments are skipped. * * @return mixed[]|string|null The token if exists, null otherwise. */ public function next($docCommentIsComment = true) { for ($i = $this->pointer; $i < $this->numTokens; $i++) { $this->pointer++; if ( $this->tokens[$i][0] === T_WHITESPACE || $this->tokens[$i][0] === T_COMMENT || ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT) ) { continue; } return $this->tokens[$i]; } return null; } /** * Parses a single use statement. * * @return array<string, string> A list with all found class names for a use statement. */ public function parseUseStatement() { $groupRoot = ''; $class = ''; $alias = ''; $statements = []; $explicitAlias = false; while (($token = $this->next())) { if (! $explicitAlias && $token[0] === T_STRING) { $class .= $token[1]; $alias = $token[1]; } elseif ($explicitAlias && $token[0] === T_STRING) { $alias = $token[1]; } elseif ( PHP_VERSION_ID >= 80000 && ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) ) { $class .= $token[1]; $classSplit = explode('\\', $token[1]); $alias = $classSplit[count($classSplit) - 1]; } elseif ($token[0] === T_NS_SEPARATOR) { $class .= '\\'; $alias = ''; } elseif ($token[0] === T_AS) { $explicitAlias = true; $alias = ''; } elseif ($token === ',') { $statements[strtolower($alias)] = $groupRoot . $class; $class = ''; $alias = ''; $explicitAlias = false; } elseif ($token === ';') { $statements[strtolower($alias)] = $groupRoot . $class; break; } elseif ($token === '{') { $groupRoot = $class; $class = ''; } elseif ($token === '}') { continue; } else { break; } } return $statements; } /** * Gets all use statements. * * @param string $namespaceName The namespace name of the reflected class. * * @return array<string, string> A list with all found use statements. */ public function parseUseStatements($namespaceName) { $statements = []; while (($token = $this->next())) { if ($token[0] === T_USE) { $statements = array_merge($statements, $this->parseUseStatement()); continue; } if ($token[0] !== T_NAMESPACE || $this->parseNamespace() !== $namespaceName) { continue; } // Get fresh array for new namespace. This is to prevent the parser to collect the use statements // for a previous namespace with the same name. This is the case if a namespace is defined twice // or if a namespace with the same name is commented out. $statements = []; } return $statements; } /** * Gets the namespace. * * @return string The found namespace. */ public function parseNamespace() { $name = ''; while ( ($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || ( PHP_VERSION_ID >= 80000 && ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) )) ) { $name .= $token[1]; } return $name; } /** * Gets the class name. * * @return string The found class name. */ public function parseClass() { // Namespaces and class names are tokenized the same: T_STRINGs // separated by T_NS_SEPARATOR so we can use one function to provide // both. return $this->parseNamespace(); } } ast.js 0000644 00000011170 15120345746 0005700 0 ustar 00 /* Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ (function () { 'use strict'; function isExpression(node) { if (node == null) { return false; } switch (node.type) { case 'ArrayExpression': case 'AssignmentExpression': case 'BinaryExpression': case 'CallExpression': case 'ConditionalExpression': case 'FunctionExpression': case 'Identifier': case 'Literal': case 'LogicalExpression': case 'MemberExpression': case 'NewExpression': case 'ObjectExpression': case 'SequenceExpression': case 'ThisExpression': case 'UnaryExpression': case 'UpdateExpression': return true; } return false; } function isIterationStatement(node) { if (node == null) { return false; } switch (node.type) { case 'DoWhileStatement': case 'ForInStatement': case 'ForStatement': case 'WhileStatement': return true; } return false; } function isStatement(node) { if (node == null) { return false; } switch (node.type) { case 'BlockStatement': case 'BreakStatement': case 'ContinueStatement': case 'DebuggerStatement': case 'DoWhileStatement': case 'EmptyStatement': case 'ExpressionStatement': case 'ForInStatement': case 'ForStatement': case 'IfStatement': case 'LabeledStatement': case 'ReturnStatement': case 'SwitchStatement': case 'ThrowStatement': case 'TryStatement': case 'VariableDeclaration': case 'WhileStatement': case 'WithStatement': return true; } return false; } function isSourceElement(node) { return isStatement(node) || node != null && node.type === 'FunctionDeclaration'; } function trailingStatement(node) { switch (node.type) { case 'IfStatement': if (node.alternate != null) { return node.alternate; } return node.consequent; case 'LabeledStatement': case 'ForStatement': case 'ForInStatement': case 'WhileStatement': case 'WithStatement': return node.body; } return null; } function isProblematicIfStatement(node) { var current; if (node.type !== 'IfStatement') { return false; } if (node.alternate == null) { return false; } current = node.consequent; do { if (current.type === 'IfStatement') { if (current.alternate == null) { return true; } } current = trailingStatement(current); } while (current); return false; } module.exports = { isExpression: isExpression, isStatement: isStatement, isIterationStatement: isIterationStatement, isSourceElement: isSourceElement, isProblematicIfStatement: isProblematicIfStatement, trailingStatement: trailingStatement }; }()); /* vim: set sw=4 ts=4 et tw=80 : */ code.js 0000644 00000071652 15120345746 0006036 0 ustar 00 /* Copyright (C) 2013-2014 Yusuke Suzuki <utatane.tea@gmail.com> Copyright (C) 2014 Ivan Nikulin <ifaaan@gmail.com> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ (function () { 'use strict'; var ES6Regex, ES5Regex, NON_ASCII_WHITESPACES, IDENTIFIER_START, IDENTIFIER_PART, ch; // See `tools/generate-identifier-regex.js`. ES5Regex = { // ECMAScript 5.1/Unicode v9.0.0 NonAsciiIdentifierStart: NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/, // ECMAScript 5.1/Unicode v9.0.0 NonAsciiIdentifierPart: NonAsciiIdentifierPart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/ }; ES6Regex = { // ECMAScript 6/Unicode v9.0.0 NonAsciiIdentifierStart: NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/, // ECMAScript 6/Unicode v9.0.0 NonAsciiIdentifierPart: NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ }; function isDecimalDigit(ch) { return 0x30 <= ch && ch <= 0x39; // 0..9 } function isHexDigit(ch) { return 0x30 <= ch && ch <= 0x39 || // 0..9 0x61 <= ch && ch <= 0x66 || // a..f 0x41 <= ch && ch <= 0x46; // A..F } function isOctalDigit(ch) { return ch >= 0x30 && ch <= 0x37; // 0..7 } // 7.2 White Space NON_ASCII_WHITESPACES = [ 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF ]; function isWhiteSpace(ch) { return ch === 0x20 || ch === 0x09 || ch === 0x0B || ch === 0x0C || ch === 0xA0 || ch >= 0x1680 && NON_ASCII_WHITESPACES.indexOf(ch) >= 0; } // 7.3 Line Terminators function isLineTerminator(ch) { return ch === 0x0A || ch === 0x0D || ch === 0x2028 || ch === 0x2029; } // 7.6 Identifier Names and Identifiers function fromCodePoint(cp) { if (cp <= 0xFFFF) { return String.fromCharCode(cp); } var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800); var cu2 = String.fromCharCode(((cp - 0x10000) % 0x400) + 0xDC00); return cu1 + cu2; } IDENTIFIER_START = new Array(0x80); for(ch = 0; ch < 0x80; ++ch) { IDENTIFIER_START[ch] = ch >= 0x61 && ch <= 0x7A || // a..z ch >= 0x41 && ch <= 0x5A || // A..Z ch === 0x24 || ch === 0x5F; // $ (dollar) and _ (underscore) } IDENTIFIER_PART = new Array(0x80); for(ch = 0; ch < 0x80; ++ch) { IDENTIFIER_PART[ch] = ch >= 0x61 && ch <= 0x7A || // a..z ch >= 0x41 && ch <= 0x5A || // A..Z ch >= 0x30 && ch <= 0x39 || // 0..9 ch === 0x24 || ch === 0x5F; // $ (dollar) and _ (underscore) } function isIdentifierStartES5(ch) { return ch < 0x80 ? IDENTIFIER_START[ch] : ES5Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch)); } function isIdentifierPartES5(ch) { return ch < 0x80 ? IDENTIFIER_PART[ch] : ES5Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch)); } function isIdentifierStartES6(ch) { return ch < 0x80 ? IDENTIFIER_START[ch] : ES6Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch)); } function isIdentifierPartES6(ch) { return ch < 0x80 ? IDENTIFIER_PART[ch] : ES6Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch)); } module.exports = { isDecimalDigit: isDecimalDigit, isHexDigit: isHexDigit, isOctalDigit: isOctalDigit, isWhiteSpace: isWhiteSpace, isLineTerminator: isLineTerminator, isIdentifierStartES5: isIdentifierStartES5, isIdentifierPartES5: isIdentifierPartES5, isIdentifierStartES6: isIdentifierStartES6, isIdentifierPartES6: isIdentifierPartES6 }; }()); /* vim: set sw=4 ts=4 et tw=80 : */ keyword.js 0000644 00000012760 15120345746 0006603 0 ustar 00 /* Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ (function () { 'use strict'; var code = require('./code'); function isStrictModeReservedWordES6(id) { switch (id) { case 'implements': case 'interface': case 'package': case 'private': case 'protected': case 'public': case 'static': case 'let': return true; default: return false; } } function isKeywordES5(id, strict) { // yield should not be treated as keyword under non-strict mode. if (!strict && id === 'yield') { return false; } return isKeywordES6(id, strict); } function isKeywordES6(id, strict) { if (strict && isStrictModeReservedWordES6(id)) { return true; } switch (id.length) { case 2: return (id === 'if') || (id === 'in') || (id === 'do'); case 3: return (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try'); case 4: return (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with') || (id === 'enum'); case 5: return (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw') || (id === 'const') || (id === 'yield') || (id === 'class') || (id === 'super'); case 6: return (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch') || (id === 'export') || (id === 'import'); case 7: return (id === 'default') || (id === 'finally') || (id === 'extends'); case 8: return (id === 'function') || (id === 'continue') || (id === 'debugger'); case 10: return (id === 'instanceof'); default: return false; } } function isReservedWordES5(id, strict) { return id === 'null' || id === 'true' || id === 'false' || isKeywordES5(id, strict); } function isReservedWordES6(id, strict) { return id === 'null' || id === 'true' || id === 'false' || isKeywordES6(id, strict); } function isRestrictedWord(id) { return id === 'eval' || id === 'arguments'; } function isIdentifierNameES5(id) { var i, iz, ch; if (id.length === 0) { return false; } ch = id.charCodeAt(0); if (!code.isIdentifierStartES5(ch)) { return false; } for (i = 1, iz = id.length; i < iz; ++i) { ch = id.charCodeAt(i); if (!code.isIdentifierPartES5(ch)) { return false; } } return true; } function decodeUtf16(lead, trail) { return (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000; } function isIdentifierNameES6(id) { var i, iz, ch, lowCh, check; if (id.length === 0) { return false; } check = code.isIdentifierStartES6; for (i = 0, iz = id.length; i < iz; ++i) { ch = id.charCodeAt(i); if (0xD800 <= ch && ch <= 0xDBFF) { ++i; if (i >= iz) { return false; } lowCh = id.charCodeAt(i); if (!(0xDC00 <= lowCh && lowCh <= 0xDFFF)) { return false; } ch = decodeUtf16(ch, lowCh); } if (!check(ch)) { return false; } check = code.isIdentifierPartES6; } return true; } function isIdentifierES5(id, strict) { return isIdentifierNameES5(id) && !isReservedWordES5(id, strict); } function isIdentifierES6(id, strict) { return isIdentifierNameES6(id) && !isReservedWordES6(id, strict); } module.exports = { isKeywordES5: isKeywordES5, isKeywordES6: isKeywordES6, isReservedWordES5: isReservedWordES5, isReservedWordES6: isReservedWordES6, isRestrictedWord: isRestrictedWord, isIdentifierNameES5: isIdentifierNameES5, isIdentifierNameES6: isIdentifierNameES6, isIdentifierES5: isIdentifierES5, isIdentifierES6: isIdentifierES6 }; }()); /* vim: set sw=4 ts=4 et tw=80 : */ utils.js 0000644 00000002767 15120345746 0006265 0 ustar 00 /* Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ (function () { 'use strict'; exports.ast = require('./ast'); exports.code = require('./code'); exports.keyword = require('./keyword'); }()); /* vim: set sw=4 ts=4 et tw=80 : */
Coded With 💗 by
0x6ick