update
This commit is contained in:
parent
56fa52fd80
commit
59f287112f
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
|||||||
data/
|
data/
|
||||||
data_bak/
|
data_bak/
|
||||||
|
static-bak
|
||||||
|
static-files
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
'use strict';
|
||||||
|
const path = require('path');
|
||||||
|
const binaryExtensions = require('binary-extensions');
|
||||||
|
|
||||||
|
const extensions = new Set(binaryExtensions);
|
||||||
|
|
||||||
|
module.exports = filePath => extensions.has(path.extname(filePath).slice(1).toLowerCase());
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
1.0.0 / 2024-08-31
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Drop support for node <18
|
||||||
|
* Added an option preferred encodings array #59
|
||||||
|
|
||||||
|
0.6.3 / 2022-01-22
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Revert "Lazy-load modules from main entry point"
|
||||||
|
|
||||||
|
0.6.2 / 2019-04-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix sorting charset, encoding, and language with extra parameters
|
||||||
|
|
||||||
|
0.6.1 / 2016-05-02
|
||||||
|
==================
|
||||||
|
|
||||||
|
* perf: improve `Accept` parsing speed
|
||||||
|
* perf: improve `Accept-Charset` parsing speed
|
||||||
|
* perf: improve `Accept-Encoding` parsing speed
|
||||||
|
* perf: improve `Accept-Language` parsing speed
|
||||||
|
|
||||||
|
0.6.0 / 2015-09-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix including type extensions in parameters in `Accept` parsing
|
||||||
|
* Fix parsing `Accept` parameters with quoted equals
|
||||||
|
* Fix parsing `Accept` parameters with quoted semicolons
|
||||||
|
* Lazy-load modules from main entry point
|
||||||
|
* perf: delay type concatenation until needed
|
||||||
|
* perf: enable strict mode
|
||||||
|
* perf: hoist regular expressions
|
||||||
|
* perf: remove closures getting spec properties
|
||||||
|
* perf: remove a closure from media type parsing
|
||||||
|
* perf: remove property delete from media type parsing
|
||||||
|
|
||||||
|
0.5.3 / 2015-05-10
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix media type parameter matching to be case-insensitive
|
||||||
|
|
||||||
|
0.5.2 / 2015-05-06
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix comparing media types with quoted values
|
||||||
|
* Fix splitting media types with quoted commas
|
||||||
|
|
||||||
|
0.5.1 / 2015-02-14
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix preference sorting to be stable for long acceptable lists
|
||||||
|
|
||||||
|
0.5.0 / 2014-12-18
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix list return order when large accepted list
|
||||||
|
* Fix missing identity encoding when q=0 exists
|
||||||
|
* Remove dynamic building of Negotiator class
|
||||||
|
|
||||||
|
0.4.9 / 2014-10-14
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix error when media type has invalid parameter
|
||||||
|
|
||||||
|
0.4.8 / 2014-09-28
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix all negotiations to be case-insensitive
|
||||||
|
* Stable sort preferences of same quality according to client order
|
||||||
|
* Support Node.js 0.6
|
||||||
|
|
||||||
|
0.4.7 / 2014-06-24
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Handle invalid provided languages
|
||||||
|
* Handle invalid provided media types
|
||||||
|
|
||||||
|
0.4.6 / 2014-06-11
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Order by specificity when quality is the same
|
||||||
|
|
||||||
|
0.4.5 / 2014-05-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix regression in empty header handling
|
||||||
|
|
||||||
|
0.4.4 / 2014-05-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix behaviors when headers are not present
|
||||||
|
|
||||||
|
0.4.3 / 2014-04-16
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Handle slashes on media params correctly
|
||||||
|
|
||||||
|
0.4.2 / 2014-02-28
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix media type sorting
|
||||||
|
* Handle media types params strictly
|
||||||
|
|
||||||
|
0.4.1 / 2014-01-16
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Use most specific matches
|
||||||
|
|
||||||
|
0.4.0 / 2014-01-09
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Remove preferred prefix from methods
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,43 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var $TypeError = require('es-errors/type');
|
||||||
|
var inspect = require('object-inspect');
|
||||||
|
var getSideChannelList = require('side-channel-list');
|
||||||
|
var getSideChannelMap = require('side-channel-map');
|
||||||
|
var getSideChannelWeakMap = require('side-channel-weakmap');
|
||||||
|
|
||||||
|
var makeChannel = getSideChannelWeakMap || getSideChannelMap || getSideChannelList;
|
||||||
|
|
||||||
|
/** @type {import('.')} */
|
||||||
|
module.exports = function getSideChannel() {
|
||||||
|
/** @typedef {ReturnType<typeof getSideChannel>} Channel */
|
||||||
|
|
||||||
|
/** @type {Channel | undefined} */ var $channelData;
|
||||||
|
|
||||||
|
/** @type {Channel} */
|
||||||
|
var channel = {
|
||||||
|
assert: function (key) {
|
||||||
|
if (!channel.has(key)) {
|
||||||
|
throw new $TypeError('Side channel does not contain ' + inspect(key));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'delete': function (key) {
|
||||||
|
return !!$channelData && $channelData['delete'](key);
|
||||||
|
},
|
||||||
|
get: function (key) {
|
||||||
|
return $channelData && $channelData.get(key);
|
||||||
|
},
|
||||||
|
has: function (key) {
|
||||||
|
return !!$channelData && $channelData.has(key);
|
||||||
|
},
|
||||||
|
set: function (key, value) {
|
||||||
|
if (!$channelData) {
|
||||||
|
$channelData = makeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
$channelData.set(key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// @ts-expect-error TODO: figure out why this is erroring
|
||||||
|
return channel;
|
||||||
|
};
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,4 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/** @type {import('./reflectApply')} */
|
||||||
|
module.exports = typeof Reflect !== 'undefined' && Reflect && Reflect.apply;
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "object-assign",
|
||||||
|
"version": "4.1.1",
|
||||||
|
"description": "ES2015 `Object.assign()` ponyfill",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "sindresorhus/object-assign",
|
||||||
|
"author": {
|
||||||
|
"name": "Sindre Sorhus",
|
||||||
|
"email": "sindresorhus@gmail.com",
|
||||||
|
"url": "sindresorhus.com"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "xo && ava",
|
||||||
|
"bench": "matcha bench.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"object",
|
||||||
|
"assign",
|
||||||
|
"extend",
|
||||||
|
"properties",
|
||||||
|
"es2015",
|
||||||
|
"ecmascript",
|
||||||
|
"harmony",
|
||||||
|
"ponyfill",
|
||||||
|
"prollyfill",
|
||||||
|
"polyfill",
|
||||||
|
"shim",
|
||||||
|
"browser"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"ava": "^0.16.0",
|
||||||
|
"lodash": "^4.16.4",
|
||||||
|
"matcha": "^0.7.0",
|
||||||
|
"xo": "^0.16.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,294 @@
|
|||||||
|
/**
|
||||||
|
* negotiator
|
||||||
|
* Copyright(c) 2012 Isaac Z. Schlueter
|
||||||
|
* Copyright(c) 2014 Federico Romero
|
||||||
|
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = preferredMediaTypes;
|
||||||
|
module.exports.preferredMediaTypes = preferredMediaTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module variables.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var simpleMediaTypeRegExp = /^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the Accept header.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parseAccept(accept) {
|
||||||
|
var accepts = splitMediaTypes(accept);
|
||||||
|
|
||||||
|
for (var i = 0, j = 0; i < accepts.length; i++) {
|
||||||
|
var mediaType = parseMediaType(accepts[i].trim(), i);
|
||||||
|
|
||||||
|
if (mediaType) {
|
||||||
|
accepts[j++] = mediaType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim accepts
|
||||||
|
accepts.length = j;
|
||||||
|
|
||||||
|
return accepts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a media type from the Accept header.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parseMediaType(str, i) {
|
||||||
|
var match = simpleMediaTypeRegExp.exec(str);
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
var params = Object.create(null);
|
||||||
|
var q = 1;
|
||||||
|
var subtype = match[2];
|
||||||
|
var type = match[1];
|
||||||
|
|
||||||
|
if (match[3]) {
|
||||||
|
var kvps = splitParameters(match[3]).map(splitKeyValuePair);
|
||||||
|
|
||||||
|
for (var j = 0; j < kvps.length; j++) {
|
||||||
|
var pair = kvps[j];
|
||||||
|
var key = pair[0].toLowerCase();
|
||||||
|
var val = pair[1];
|
||||||
|
|
||||||
|
// get the value, unwrapping quotes
|
||||||
|
var value = val && val[0] === '"' && val[val.length - 1] === '"'
|
||||||
|
? val.slice(1, -1)
|
||||||
|
: val;
|
||||||
|
|
||||||
|
if (key === 'q') {
|
||||||
|
q = parseFloat(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store parameter
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: type,
|
||||||
|
subtype: subtype,
|
||||||
|
params: params,
|
||||||
|
q: q,
|
||||||
|
i: i
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the priority of a media type.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getMediaTypePriority(type, accepted, index) {
|
||||||
|
var priority = {o: -1, q: 0, s: 0};
|
||||||
|
|
||||||
|
for (var i = 0; i < accepted.length; i++) {
|
||||||
|
var spec = specify(type, accepted[i], index);
|
||||||
|
|
||||||
|
if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
|
||||||
|
priority = spec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specificity of the media type.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function specify(type, spec, index) {
|
||||||
|
var p = parseMediaType(type);
|
||||||
|
var s = 0;
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(spec.type.toLowerCase() == p.type.toLowerCase()) {
|
||||||
|
s |= 4
|
||||||
|
} else if(spec.type != '*') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(spec.subtype.toLowerCase() == p.subtype.toLowerCase()) {
|
||||||
|
s |= 2
|
||||||
|
} else if(spec.subtype != '*') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = Object.keys(spec.params);
|
||||||
|
if (keys.length > 0) {
|
||||||
|
if (keys.every(function (k) {
|
||||||
|
return spec.params[k] == '*' || (spec.params[k] || '').toLowerCase() == (p.params[k] || '').toLowerCase();
|
||||||
|
})) {
|
||||||
|
s |= 1
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
i: index,
|
||||||
|
o: spec.i,
|
||||||
|
q: spec.q,
|
||||||
|
s: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the preferred media types from an Accept header.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function preferredMediaTypes(accept, provided) {
|
||||||
|
// RFC 2616 sec 14.2: no header = */*
|
||||||
|
var accepts = parseAccept(accept === undefined ? '*/*' : accept || '');
|
||||||
|
|
||||||
|
if (!provided) {
|
||||||
|
// sorted list of all types
|
||||||
|
return accepts
|
||||||
|
.filter(isQuality)
|
||||||
|
.sort(compareSpecs)
|
||||||
|
.map(getFullType);
|
||||||
|
}
|
||||||
|
|
||||||
|
var priorities = provided.map(function getPriority(type, index) {
|
||||||
|
return getMediaTypePriority(type, accepts, index);
|
||||||
|
});
|
||||||
|
|
||||||
|
// sorted list of accepted types
|
||||||
|
return priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) {
|
||||||
|
return provided[priorities.indexOf(priority)];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two specs.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function compareSpecs(a, b) {
|
||||||
|
return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get full type string.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getFullType(spec) {
|
||||||
|
return spec.type + '/' + spec.subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a spec has any quality.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isQuality(spec) {
|
||||||
|
return spec.q > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of quotes in a string.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function quoteCount(string) {
|
||||||
|
var count = 0;
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
while ((index = string.indexOf('"', index)) !== -1) {
|
||||||
|
count++;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a key value pair.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function splitKeyValuePair(str) {
|
||||||
|
var index = str.indexOf('=');
|
||||||
|
var key;
|
||||||
|
var val;
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
key = str;
|
||||||
|
} else {
|
||||||
|
key = str.slice(0, index);
|
||||||
|
val = str.slice(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [key, val];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split an Accept header into media types.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function splitMediaTypes(accept) {
|
||||||
|
var accepts = accept.split(',');
|
||||||
|
|
||||||
|
for (var i = 1, j = 0; i < accepts.length; i++) {
|
||||||
|
if (quoteCount(accepts[j]) % 2 == 0) {
|
||||||
|
accepts[++j] = accepts[i];
|
||||||
|
} else {
|
||||||
|
accepts[j] += ',' + accepts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim accepts
|
||||||
|
accepts.length = j + 1;
|
||||||
|
|
||||||
|
return accepts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a string of parameters.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function splitParameters(str) {
|
||||||
|
var parameters = str.split(';');
|
||||||
|
|
||||||
|
for (var i = 1, j = 0; i < parameters.length; i++) {
|
||||||
|
if (quoteCount(parameters[j]) % 2 == 0) {
|
||||||
|
parameters[++j] = parameters[i];
|
||||||
|
} else {
|
||||||
|
parameters[j] += ';' + parameters[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim parameters
|
||||||
|
parameters.length = j + 1;
|
||||||
|
|
||||||
|
for (var i = 0; i < parameters.length; i++) {
|
||||||
|
parameters[i] = parameters[i].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "binary-extensions",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "List of binary file extensions",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "sindresorhus/binary-extensions",
|
||||||
|
"funding": "https://github.com/sponsors/sindresorhus",
|
||||||
|
"author": {
|
||||||
|
"name": "Sindre Sorhus",
|
||||||
|
"email": "sindresorhus@gmail.com",
|
||||||
|
"url": "https://sindresorhus.com"
|
||||||
|
},
|
||||||
|
"sideEffects": false,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "xo && ava && tsd"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.d.ts",
|
||||||
|
"binary-extensions.json",
|
||||||
|
"binary-extensions.json.d.ts"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"binary",
|
||||||
|
"extensions",
|
||||||
|
"extension",
|
||||||
|
"file",
|
||||||
|
"json",
|
||||||
|
"list",
|
||||||
|
"array"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"ava": "^1.4.1",
|
||||||
|
"tsd": "^0.7.2",
|
||||||
|
"xo": "^0.24.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,287 @@
|
|||||||
|
const minimatch = require('minimatch');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const debug = require('debug')('nodemon:match');
|
||||||
|
const utils = require('../utils');
|
||||||
|
|
||||||
|
module.exports = match;
|
||||||
|
module.exports.rulesToMonitor = rulesToMonitor;
|
||||||
|
|
||||||
|
function rulesToMonitor(watch, ignore, config) {
|
||||||
|
var monitor = [];
|
||||||
|
|
||||||
|
if (!Array.isArray(ignore)) {
|
||||||
|
if (ignore) {
|
||||||
|
ignore = [ignore];
|
||||||
|
} else {
|
||||||
|
ignore = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(watch)) {
|
||||||
|
if (watch) {
|
||||||
|
watch = [watch];
|
||||||
|
} else {
|
||||||
|
watch = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watch && watch.length) {
|
||||||
|
monitor = utils.clone(watch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignore) {
|
||||||
|
[].push.apply(
|
||||||
|
monitor,
|
||||||
|
(ignore || []).map(function (rule) {
|
||||||
|
return '!' + rule;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cwd = process.cwd();
|
||||||
|
|
||||||
|
// next check if the monitored paths are actual directories
|
||||||
|
// or just patterns - and expand the rule to include *.*
|
||||||
|
monitor = monitor.map(function (rule) {
|
||||||
|
var not = rule.slice(0, 1) === '!';
|
||||||
|
|
||||||
|
if (not) {
|
||||||
|
rule = rule.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule === '.' || rule === '.*') {
|
||||||
|
rule = '*.*';
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = path.resolve(cwd, rule);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var stat = fs.statSync(dir);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
rule = dir;
|
||||||
|
if (rule.slice(-1) !== '/') {
|
||||||
|
rule += '/';
|
||||||
|
}
|
||||||
|
rule += '**/*';
|
||||||
|
|
||||||
|
// `!not` ... sorry.
|
||||||
|
if (!not) {
|
||||||
|
config.dirs.push(dir);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ensures we end up in the check that tries to get a base directory
|
||||||
|
// and then adds it to the watch list
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
var base = tryBaseDir(dir);
|
||||||
|
if (!not && base) {
|
||||||
|
if (config.dirs.indexOf(base) === -1) {
|
||||||
|
config.dirs.push(base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.slice(-1) === '/') {
|
||||||
|
// just slap on a * anyway
|
||||||
|
rule += '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the url ends with * but not **/* and not *.*
|
||||||
|
// then convert to **/* - somehow it was missed :-\
|
||||||
|
if (
|
||||||
|
rule.slice(-4) !== '**/*' &&
|
||||||
|
rule.slice(-1) === '*' &&
|
||||||
|
rule.indexOf('*.') === -1
|
||||||
|
) {
|
||||||
|
if (rule.slice(-2) !== '**') {
|
||||||
|
rule += '*/*';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (not ? '!' : '') + rule;
|
||||||
|
});
|
||||||
|
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryBaseDir(dir) {
|
||||||
|
var stat;
|
||||||
|
if (/[?*\{\[]+/.test(dir)) {
|
||||||
|
// if this is pattern, then try to find the base
|
||||||
|
try {
|
||||||
|
var base = path.dirname(dir.replace(/([?*\{\[]+.*$)/, 'foo'));
|
||||||
|
stat = fs.statSync(base);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// console.log(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
stat = fs.statSync(dir);
|
||||||
|
// if this path is actually a single file that exists, then just monitor
|
||||||
|
// that, *specifically*.
|
||||||
|
if (stat.isFile() || stat.isDirectory()) {
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function match(files, monitor, ext) {
|
||||||
|
// sort the rules by highest specificity (based on number of slashes)
|
||||||
|
// ignore rules (!) get sorted highest as they take precedent
|
||||||
|
const cwd = process.cwd();
|
||||||
|
var rules = monitor
|
||||||
|
.sort(function (a, b) {
|
||||||
|
var r = b.split(path.sep).length - a.split(path.sep).length;
|
||||||
|
var aIsIgnore = a.slice(0, 1) === '!';
|
||||||
|
var bIsIgnore = b.slice(0, 1) === '!';
|
||||||
|
|
||||||
|
if (aIsIgnore || bIsIgnore) {
|
||||||
|
if (aIsIgnore) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r === 0) {
|
||||||
|
return b.length - a.length;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
})
|
||||||
|
.map(function (s) {
|
||||||
|
var prefix = s.slice(0, 1);
|
||||||
|
|
||||||
|
if (prefix === '!') {
|
||||||
|
if (s.indexOf('!' + cwd) === 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it starts with a period, then let's get the relative path
|
||||||
|
if (s.indexOf('!.') === 0) {
|
||||||
|
return '!' + path.resolve(cwd, s.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return '!**' + (prefix !== path.sep ? path.sep : '') + s.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it starts with a period, then let's get the relative path
|
||||||
|
if (s.indexOf('.') === 0) {
|
||||||
|
return path.resolve(cwd, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.indexOf(cwd) === 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '**' + (prefix !== path.sep ? path.sep : '') + s;
|
||||||
|
});
|
||||||
|
|
||||||
|
debug('rules', rules);
|
||||||
|
|
||||||
|
var good = [];
|
||||||
|
var whitelist = []; // files that we won't check against the extension
|
||||||
|
var ignored = 0;
|
||||||
|
var watched = 0;
|
||||||
|
var usedRules = [];
|
||||||
|
var minimatchOpts = {
|
||||||
|
dot: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// enable case-insensitivity on Windows
|
||||||
|
if (utils.isWindows) {
|
||||||
|
minimatchOpts.nocase = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.forEach(function (file) {
|
||||||
|
file = path.resolve(cwd, file);
|
||||||
|
|
||||||
|
var matched = false;
|
||||||
|
for (var i = 0; i < rules.length; i++) {
|
||||||
|
if (rules[i].slice(0, 1) === '!') {
|
||||||
|
if (!minimatch(file, rules[i], minimatchOpts)) {
|
||||||
|
debug('ignored', file, 'rule:', rules[i]);
|
||||||
|
ignored++;
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug('matched', file, 'rule:', rules[i]);
|
||||||
|
if (minimatch(file, rules[i], minimatchOpts)) {
|
||||||
|
watched++;
|
||||||
|
|
||||||
|
// don't repeat the output if a rule is matched
|
||||||
|
if (usedRules.indexOf(rules[i]) === -1) {
|
||||||
|
usedRules.push(rules[i]);
|
||||||
|
utils.log.detail('matched rule: ' + rules[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the rule doesn't match the WATCH EVERYTHING
|
||||||
|
// but *does* match a rule that ends with *.*, then
|
||||||
|
// white list it - in that we don't run it through
|
||||||
|
// the extension check too.
|
||||||
|
if (
|
||||||
|
rules[i] !== '**' + path.sep + '*.*' &&
|
||||||
|
rules[i].slice(-3) === '*.*'
|
||||||
|
) {
|
||||||
|
whitelist.push(file);
|
||||||
|
} else if (path.basename(file) === path.basename(rules[i])) {
|
||||||
|
// if the file matches the actual rule, then it's put on whitelist
|
||||||
|
whitelist.push(file);
|
||||||
|
} else {
|
||||||
|
good.push(file);
|
||||||
|
}
|
||||||
|
matched = true;
|
||||||
|
} else {
|
||||||
|
// utils.log.detail('no match: ' + rules[i], file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matched) {
|
||||||
|
ignored++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// finally check the good files against the extensions that we're monitoring
|
||||||
|
if (ext) {
|
||||||
|
if (ext.indexOf(',') === -1) {
|
||||||
|
ext = '**/*.' + ext;
|
||||||
|
} else {
|
||||||
|
ext = '**/*.{' + ext + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
good = good.filter(function (file) {
|
||||||
|
// only compare the filename to the extension test
|
||||||
|
return minimatch(path.basename(file), ext, minimatchOpts);
|
||||||
|
});
|
||||||
|
debug('good (filtered by ext)', good);
|
||||||
|
} else {
|
||||||
|
// else assume *.*
|
||||||
|
debug('good', good);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (whitelist.length) debug('whitelist', whitelist);
|
||||||
|
|
||||||
|
var result = good.concat(whitelist);
|
||||||
|
|
||||||
|
if (utils.isWindows) {
|
||||||
|
// fix for windows testing - I *think* this is okay to do
|
||||||
|
result = result.map(function (file) {
|
||||||
|
return file.slice(0, 1).toLowerCase() + file.slice(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: result,
|
||||||
|
ignored: ignored,
|
||||||
|
watched: watched,
|
||||||
|
total: files.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,319 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const debug = require('../internal/debug')
|
||||||
|
const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')
|
||||||
|
const { safeRe: re, t } = require('../internal/re')
|
||||||
|
|
||||||
|
const parseOptions = require('../internal/parse-options')
|
||||||
|
const { compareIdentifiers } = require('../internal/identifiers')
|
||||||
|
class SemVer {
|
||||||
|
constructor (version, options) {
|
||||||
|
options = parseOptions(options)
|
||||||
|
|
||||||
|
if (version instanceof SemVer) {
|
||||||
|
if (version.loose === !!options.loose &&
|
||||||
|
version.includePrerelease === !!options.includePrerelease) {
|
||||||
|
return version
|
||||||
|
} else {
|
||||||
|
version = version.version
|
||||||
|
}
|
||||||
|
} else if (typeof version !== 'string') {
|
||||||
|
throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.length > MAX_LENGTH) {
|
||||||
|
throw new TypeError(
|
||||||
|
`version is longer than ${MAX_LENGTH} characters`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('SemVer', version, options)
|
||||||
|
this.options = options
|
||||||
|
this.loose = !!options.loose
|
||||||
|
// this isn't actually relevant for versions, but keep it so that we
|
||||||
|
// don't run into trouble passing this.options around.
|
||||||
|
this.includePrerelease = !!options.includePrerelease
|
||||||
|
|
||||||
|
const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
throw new TypeError(`Invalid Version: ${version}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.raw = version
|
||||||
|
|
||||||
|
// these are actually numbers
|
||||||
|
this.major = +m[1]
|
||||||
|
this.minor = +m[2]
|
||||||
|
this.patch = +m[3]
|
||||||
|
|
||||||
|
if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
|
||||||
|
throw new TypeError('Invalid major version')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
|
||||||
|
throw new TypeError('Invalid minor version')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
|
||||||
|
throw new TypeError('Invalid patch version')
|
||||||
|
}
|
||||||
|
|
||||||
|
// numberify any prerelease numeric ids
|
||||||
|
if (!m[4]) {
|
||||||
|
this.prerelease = []
|
||||||
|
} else {
|
||||||
|
this.prerelease = m[4].split('.').map((id) => {
|
||||||
|
if (/^[0-9]+$/.test(id)) {
|
||||||
|
const num = +id
|
||||||
|
if (num >= 0 && num < MAX_SAFE_INTEGER) {
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.build = m[5] ? m[5].split('.') : []
|
||||||
|
this.format()
|
||||||
|
}
|
||||||
|
|
||||||
|
format () {
|
||||||
|
this.version = `${this.major}.${this.minor}.${this.patch}`
|
||||||
|
if (this.prerelease.length) {
|
||||||
|
this.version += `-${this.prerelease.join('.')}`
|
||||||
|
}
|
||||||
|
return this.version
|
||||||
|
}
|
||||||
|
|
||||||
|
toString () {
|
||||||
|
return this.version
|
||||||
|
}
|
||||||
|
|
||||||
|
compare (other) {
|
||||||
|
debug('SemVer.compare', this.version, this.options, other)
|
||||||
|
if (!(other instanceof SemVer)) {
|
||||||
|
if (typeof other === 'string' && other === this.version) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
other = new SemVer(other, this.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other.version === this.version) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.compareMain(other) || this.comparePre(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
compareMain (other) {
|
||||||
|
if (!(other instanceof SemVer)) {
|
||||||
|
other = new SemVer(other, this.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
compareIdentifiers(this.major, other.major) ||
|
||||||
|
compareIdentifiers(this.minor, other.minor) ||
|
||||||
|
compareIdentifiers(this.patch, other.patch)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
comparePre (other) {
|
||||||
|
if (!(other instanceof SemVer)) {
|
||||||
|
other = new SemVer(other, this.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT having a prerelease is > having one
|
||||||
|
if (this.prerelease.length && !other.prerelease.length) {
|
||||||
|
return -1
|
||||||
|
} else if (!this.prerelease.length && other.prerelease.length) {
|
||||||
|
return 1
|
||||||
|
} else if (!this.prerelease.length && !other.prerelease.length) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
do {
|
||||||
|
const a = this.prerelease[i]
|
||||||
|
const b = other.prerelease[i]
|
||||||
|
debug('prerelease compare', i, a, b)
|
||||||
|
if (a === undefined && b === undefined) {
|
||||||
|
return 0
|
||||||
|
} else if (b === undefined) {
|
||||||
|
return 1
|
||||||
|
} else if (a === undefined) {
|
||||||
|
return -1
|
||||||
|
} else if (a === b) {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return compareIdentifiers(a, b)
|
||||||
|
}
|
||||||
|
} while (++i)
|
||||||
|
}
|
||||||
|
|
||||||
|
compareBuild (other) {
|
||||||
|
if (!(other instanceof SemVer)) {
|
||||||
|
other = new SemVer(other, this.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
do {
|
||||||
|
const a = this.build[i]
|
||||||
|
const b = other.build[i]
|
||||||
|
debug('build compare', i, a, b)
|
||||||
|
if (a === undefined && b === undefined) {
|
||||||
|
return 0
|
||||||
|
} else if (b === undefined) {
|
||||||
|
return 1
|
||||||
|
} else if (a === undefined) {
|
||||||
|
return -1
|
||||||
|
} else if (a === b) {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return compareIdentifiers(a, b)
|
||||||
|
}
|
||||||
|
} while (++i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// preminor will bump the version up to the next minor release, and immediately
|
||||||
|
// down to pre-release. premajor and prepatch work the same way.
|
||||||
|
inc (release, identifier, identifierBase) {
|
||||||
|
if (release.startsWith('pre')) {
|
||||||
|
if (!identifier && identifierBase === false) {
|
||||||
|
throw new Error('invalid increment argument: identifier is empty')
|
||||||
|
}
|
||||||
|
// Avoid an invalid semver results
|
||||||
|
if (identifier) {
|
||||||
|
const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE])
|
||||||
|
if (!match || match[1] !== identifier) {
|
||||||
|
throw new Error(`invalid identifier: ${identifier}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (release) {
|
||||||
|
case 'premajor':
|
||||||
|
this.prerelease.length = 0
|
||||||
|
this.patch = 0
|
||||||
|
this.minor = 0
|
||||||
|
this.major++
|
||||||
|
this.inc('pre', identifier, identifierBase)
|
||||||
|
break
|
||||||
|
case 'preminor':
|
||||||
|
this.prerelease.length = 0
|
||||||
|
this.patch = 0
|
||||||
|
this.minor++
|
||||||
|
this.inc('pre', identifier, identifierBase)
|
||||||
|
break
|
||||||
|
case 'prepatch':
|
||||||
|
// If this is already a prerelease, it will bump to the next version
|
||||||
|
// drop any prereleases that might already exist, since they are not
|
||||||
|
// relevant at this point.
|
||||||
|
this.prerelease.length = 0
|
||||||
|
this.inc('patch', identifier, identifierBase)
|
||||||
|
this.inc('pre', identifier, identifierBase)
|
||||||
|
break
|
||||||
|
// If the input is a non-prerelease version, this acts the same as
|
||||||
|
// prepatch.
|
||||||
|
case 'prerelease':
|
||||||
|
if (this.prerelease.length === 0) {
|
||||||
|
this.inc('patch', identifier, identifierBase)
|
||||||
|
}
|
||||||
|
this.inc('pre', identifier, identifierBase)
|
||||||
|
break
|
||||||
|
case 'release':
|
||||||
|
if (this.prerelease.length === 0) {
|
||||||
|
throw new Error(`version ${this.raw} is not a prerelease`)
|
||||||
|
}
|
||||||
|
this.prerelease.length = 0
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'major':
|
||||||
|
// If this is a pre-major version, bump up to the same major version.
|
||||||
|
// Otherwise increment major.
|
||||||
|
// 1.0.0-5 bumps to 1.0.0
|
||||||
|
// 1.1.0 bumps to 2.0.0
|
||||||
|
if (
|
||||||
|
this.minor !== 0 ||
|
||||||
|
this.patch !== 0 ||
|
||||||
|
this.prerelease.length === 0
|
||||||
|
) {
|
||||||
|
this.major++
|
||||||
|
}
|
||||||
|
this.minor = 0
|
||||||
|
this.patch = 0
|
||||||
|
this.prerelease = []
|
||||||
|
break
|
||||||
|
case 'minor':
|
||||||
|
// If this is a pre-minor version, bump up to the same minor version.
|
||||||
|
// Otherwise increment minor.
|
||||||
|
// 1.2.0-5 bumps to 1.2.0
|
||||||
|
// 1.2.1 bumps to 1.3.0
|
||||||
|
if (this.patch !== 0 || this.prerelease.length === 0) {
|
||||||
|
this.minor++
|
||||||
|
}
|
||||||
|
this.patch = 0
|
||||||
|
this.prerelease = []
|
||||||
|
break
|
||||||
|
case 'patch':
|
||||||
|
// If this is not a pre-release version, it will increment the patch.
|
||||||
|
// If it is a pre-release it will bump up to the same patch version.
|
||||||
|
// 1.2.0-5 patches to 1.2.0
|
||||||
|
// 1.2.0 patches to 1.2.1
|
||||||
|
if (this.prerelease.length === 0) {
|
||||||
|
this.patch++
|
||||||
|
}
|
||||||
|
this.prerelease = []
|
||||||
|
break
|
||||||
|
// This probably shouldn't be used publicly.
|
||||||
|
// 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
|
||||||
|
case 'pre': {
|
||||||
|
const base = Number(identifierBase) ? 1 : 0
|
||||||
|
|
||||||
|
if (this.prerelease.length === 0) {
|
||||||
|
this.prerelease = [base]
|
||||||
|
} else {
|
||||||
|
let i = this.prerelease.length
|
||||||
|
while (--i >= 0) {
|
||||||
|
if (typeof this.prerelease[i] === 'number') {
|
||||||
|
this.prerelease[i]++
|
||||||
|
i = -2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i === -1) {
|
||||||
|
// didn't increment anything
|
||||||
|
if (identifier === this.prerelease.join('.') && identifierBase === false) {
|
||||||
|
throw new Error('invalid increment argument: identifier already exists')
|
||||||
|
}
|
||||||
|
this.prerelease.push(base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (identifier) {
|
||||||
|
// 1.2.0-beta.1 bumps to 1.2.0-beta.2,
|
||||||
|
// 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
|
||||||
|
let prerelease = [identifier, base]
|
||||||
|
if (identifierBase === false) {
|
||||||
|
prerelease = [identifier]
|
||||||
|
}
|
||||||
|
if (compareIdentifiers(this.prerelease[0], identifier) === 0) {
|
||||||
|
if (isNaN(this.prerelease[1])) {
|
||||||
|
this.prerelease = prerelease
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.prerelease = prerelease
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`invalid increment argument: ${release}`)
|
||||||
|
}
|
||||||
|
this.raw = this.format()
|
||||||
|
if (this.build.length) {
|
||||||
|
this.raw += `+${this.build.join('.')}`
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SemVer
|
||||||
@ -0,0 +1,177 @@
|
|||||||
|
/*!
|
||||||
|
* body-parser
|
||||||
|
* Copyright(c) 2014 Jonathan Ong
|
||||||
|
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var createError = require('http-errors')
|
||||||
|
var debug = require('debug')('body-parser:urlencoded')
|
||||||
|
var isFinished = require('on-finished').isFinished
|
||||||
|
var read = require('../read')
|
||||||
|
var typeis = require('type-is')
|
||||||
|
var qs = require('qs')
|
||||||
|
var { getCharset, normalizeOptions } = require('../utils')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = urlencoded
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a middleware to parse urlencoded bodies.
|
||||||
|
*
|
||||||
|
* @param {object} [options]
|
||||||
|
* @return {function}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function urlencoded (options) {
|
||||||
|
var { inflate, limit, verify, shouldParse } = normalizeOptions(options, 'application/x-www-form-urlencoded')
|
||||||
|
|
||||||
|
var defaultCharset = options?.defaultCharset || 'utf-8'
|
||||||
|
if (defaultCharset !== 'utf-8' && defaultCharset !== 'iso-8859-1') {
|
||||||
|
throw new TypeError('option defaultCharset must be either utf-8 or iso-8859-1')
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the appropriate query parser
|
||||||
|
var queryparse = createQueryParser(options)
|
||||||
|
|
||||||
|
function parse (body, encoding) {
|
||||||
|
return body.length
|
||||||
|
? queryparse(body, encoding)
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function urlencodedParser (req, res, next) {
|
||||||
|
if (isFinished(req)) {
|
||||||
|
debug('body already parsed')
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('body' in req)) {
|
||||||
|
req.body = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip requests without bodies
|
||||||
|
if (!typeis.hasBody(req)) {
|
||||||
|
debug('skip empty body')
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('content-type %j', req.headers['content-type'])
|
||||||
|
|
||||||
|
// determine if request should be parsed
|
||||||
|
if (!shouldParse(req)) {
|
||||||
|
debug('skip parsing')
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert charset
|
||||||
|
var charset = getCharset(req) || defaultCharset
|
||||||
|
if (charset !== 'utf-8' && charset !== 'iso-8859-1') {
|
||||||
|
debug('invalid charset')
|
||||||
|
next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
|
||||||
|
charset: charset,
|
||||||
|
type: 'charset.unsupported'
|
||||||
|
}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// read
|
||||||
|
read(req, res, next, parse, debug, {
|
||||||
|
encoding: charset,
|
||||||
|
inflate,
|
||||||
|
limit,
|
||||||
|
verify
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the extended query parser.
|
||||||
|
*
|
||||||
|
* @param {object} options
|
||||||
|
*/
|
||||||
|
|
||||||
|
function createQueryParser (options) {
|
||||||
|
var extended = Boolean(options?.extended)
|
||||||
|
var parameterLimit = options?.parameterLimit !== undefined
|
||||||
|
? options?.parameterLimit
|
||||||
|
: 1000
|
||||||
|
var charsetSentinel = options?.charsetSentinel
|
||||||
|
var interpretNumericEntities = options?.interpretNumericEntities
|
||||||
|
var depth = extended ? (options?.depth !== undefined ? options?.depth : 32) : 0
|
||||||
|
|
||||||
|
if (isNaN(parameterLimit) || parameterLimit < 1) {
|
||||||
|
throw new TypeError('option parameterLimit must be a positive number')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(depth) || depth < 0) {
|
||||||
|
throw new TypeError('option depth must be a zero or a positive number')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFinite(parameterLimit)) {
|
||||||
|
parameterLimit = parameterLimit | 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return function queryparse (body, encoding) {
|
||||||
|
var paramCount = parameterCount(body, parameterLimit)
|
||||||
|
|
||||||
|
if (paramCount === undefined) {
|
||||||
|
debug('too many parameters')
|
||||||
|
throw createError(413, 'too many parameters', {
|
||||||
|
type: 'parameters.too.many'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayLimit = extended ? Math.max(100, paramCount) : 0
|
||||||
|
|
||||||
|
debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding')
|
||||||
|
try {
|
||||||
|
return qs.parse(body, {
|
||||||
|
allowPrototypes: true,
|
||||||
|
arrayLimit: arrayLimit,
|
||||||
|
depth: depth,
|
||||||
|
charsetSentinel: charsetSentinel,
|
||||||
|
interpretNumericEntities: interpretNumericEntities,
|
||||||
|
charset: encoding,
|
||||||
|
parameterLimit: parameterLimit,
|
||||||
|
strictDepth: true
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof RangeError) {
|
||||||
|
throw createError(400, 'The input exceeded the depth', {
|
||||||
|
type: 'querystring.parse.rangeError'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of parameters, stopping once limit reached
|
||||||
|
*
|
||||||
|
* @param {string} body
|
||||||
|
* @param {number} limit
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parameterCount (body, limit) {
|
||||||
|
var len = body.split('&').length
|
||||||
|
|
||||||
|
return len > limit ? undefined : len - 1
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
# hasown <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
|
||||||
|
|
||||||
|
[![github actions][actions-image]][actions-url]
|
||||||
|
[![coverage][codecov-image]][codecov-url]
|
||||||
|
[![License][license-image]][license-url]
|
||||||
|
[![Downloads][downloads-image]][downloads-url]
|
||||||
|
|
||||||
|
[![npm badge][npm-badge-png]][package-url]
|
||||||
|
|
||||||
|
A robust, ES3 compatible, "has own property" predicate.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
const assert = require('assert');
|
||||||
|
const hasOwn = require('hasown');
|
||||||
|
|
||||||
|
assert.equal(hasOwn({}, 'toString'), false);
|
||||||
|
assert.equal(hasOwn([], 'length'), true);
|
||||||
|
assert.equal(hasOwn({ a: 42 }, 'a'), true);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
Simply clone the repo, `npm install`, and run `npm test`
|
||||||
|
|
||||||
|
[package-url]: https://npmjs.org/package/hasown
|
||||||
|
[npm-version-svg]: https://versionbadg.es/inspect-js/hasown.svg
|
||||||
|
[deps-svg]: https://david-dm.org/inspect-js/hasOwn.svg
|
||||||
|
[deps-url]: https://david-dm.org/inspect-js/hasOwn
|
||||||
|
[dev-deps-svg]: https://david-dm.org/inspect-js/hasOwn/dev-status.svg
|
||||||
|
[dev-deps-url]: https://david-dm.org/inspect-js/hasOwn#info=devDependencies
|
||||||
|
[npm-badge-png]: https://nodei.co/npm/hasown.png?downloads=true&stars=true
|
||||||
|
[license-image]: https://img.shields.io/npm/l/hasown.svg
|
||||||
|
[license-url]: LICENSE
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/hasown.svg
|
||||||
|
[downloads-url]: https://npm-stat.com/charts.html?package=hasown
|
||||||
|
[codecov-image]: https://codecov.io/gh/inspect-js/hasOwn/branch/main/graphs/badge.svg
|
||||||
|
[codecov-url]: https://app.codecov.io/gh/inspect-js/hasOwn/
|
||||||
|
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/inspect-js/hasOwn
|
||||||
|
[actions-url]: https://github.com/inspect-js/hasOwn/actions
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
|
||||||
|
"extends": "@ljharb",
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
"id-length": "off",
|
||||||
|
"sort-keys": "off",
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "body-parser",
|
||||||
|
"description": "Node.js body parsing middleware",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"contributors": [
|
||||||
|
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||||
|
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "expressjs/body-parser",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "^3.1.2",
|
||||||
|
"content-type": "^1.0.5",
|
||||||
|
"debug": "^4.4.0",
|
||||||
|
"http-errors": "^2.0.0",
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
|
"on-finished": "^2.4.1",
|
||||||
|
"qs": "^6.14.0",
|
||||||
|
"raw-body": "^3.0.0",
|
||||||
|
"type-is": "^2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "8.34.0",
|
||||||
|
"eslint-config-standard": "14.1.1",
|
||||||
|
"eslint-plugin-import": "2.27.5",
|
||||||
|
"eslint-plugin-markdown": "3.0.0",
|
||||||
|
"eslint-plugin-node": "11.1.0",
|
||||||
|
"eslint-plugin-promise": "6.1.1",
|
||||||
|
"eslint-plugin-standard": "4.1.0",
|
||||||
|
"mocha": "^11.1.0",
|
||||||
|
"nyc": "^17.1.0",
|
||||||
|
"supertest": "^7.0.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib/",
|
||||||
|
"LICENSE",
|
||||||
|
"HISTORY.md",
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"test": "mocha --reporter spec --check-leaks test/",
|
||||||
|
"test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
|
||||||
|
"test-cov": "nyc --reporter=html --reporter=text npm test"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
declare function gOPD<O extends object, K extends keyof O>(obj: O, prop: K): PropertyDescriptor | undefined;
|
||||||
|
|
||||||
|
declare const fn: typeof gOPD | undefined | null;
|
||||||
|
|
||||||
|
export = fn;
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const eq = require('./eq')
|
||||||
|
const neq = require('./neq')
|
||||||
|
const gt = require('./gt')
|
||||||
|
const gte = require('./gte')
|
||||||
|
const lt = require('./lt')
|
||||||
|
const lte = require('./lte')
|
||||||
|
|
||||||
|
const cmp = (a, op, b, loose) => {
|
||||||
|
switch (op) {
|
||||||
|
case '===':
|
||||||
|
if (typeof a === 'object') {
|
||||||
|
a = a.version
|
||||||
|
}
|
||||||
|
if (typeof b === 'object') {
|
||||||
|
b = b.version
|
||||||
|
}
|
||||||
|
return a === b
|
||||||
|
|
||||||
|
case '!==':
|
||||||
|
if (typeof a === 'object') {
|
||||||
|
a = a.version
|
||||||
|
}
|
||||||
|
if (typeof b === 'object') {
|
||||||
|
b = b.version
|
||||||
|
}
|
||||||
|
return a !== b
|
||||||
|
|
||||||
|
case '':
|
||||||
|
case '=':
|
||||||
|
case '==':
|
||||||
|
return eq(a, b, loose)
|
||||||
|
|
||||||
|
case '!=':
|
||||||
|
return neq(a, b, loose)
|
||||||
|
|
||||||
|
case '>':
|
||||||
|
return gt(a, b, loose)
|
||||||
|
|
||||||
|
case '>=':
|
||||||
|
return gte(a, b, loose)
|
||||||
|
|
||||||
|
case '<':
|
||||||
|
return lt(a, b, loose)
|
||||||
|
|
||||||
|
case '<=':
|
||||||
|
return lte(a, b, loose)
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new TypeError(`Invalid operator: ${op}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = cmp
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
type RemoveFromTuple<
|
||||||
|
Tuple extends readonly unknown[],
|
||||||
|
RemoveCount extends number,
|
||||||
|
Index extends 1[] = []
|
||||||
|
> = Index["length"] extends RemoveCount
|
||||||
|
? Tuple
|
||||||
|
: Tuple extends [infer First, ...infer Rest]
|
||||||
|
? RemoveFromTuple<Rest, RemoveCount, [...Index, 1]>
|
||||||
|
: Tuple;
|
||||||
|
|
||||||
|
type ConcatTuples<
|
||||||
|
Prefix extends readonly unknown[],
|
||||||
|
Suffix extends readonly unknown[]
|
||||||
|
> = [...Prefix, ...Suffix];
|
||||||
|
|
||||||
|
type ExtractFunctionParams<T> = T extends (this: infer TThis, ...args: infer P extends readonly unknown[]) => infer R
|
||||||
|
? { thisArg: TThis; params: P; returnType: R }
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type BindFunction<
|
||||||
|
T extends (this: any, ...args: any[]) => any,
|
||||||
|
TThis,
|
||||||
|
TBoundArgs extends readonly unknown[],
|
||||||
|
ReceiverBound extends boolean
|
||||||
|
> = ExtractFunctionParams<T> extends {
|
||||||
|
thisArg: infer OrigThis;
|
||||||
|
params: infer P extends readonly unknown[];
|
||||||
|
returnType: infer R;
|
||||||
|
}
|
||||||
|
? ReceiverBound extends true
|
||||||
|
? (...args: RemoveFromTuple<P, Extract<TBoundArgs["length"], number>>) => R extends [OrigThis, ...infer Rest]
|
||||||
|
? [TThis, ...Rest] // Replace `this` with `thisArg`
|
||||||
|
: R
|
||||||
|
: <U, RemainingArgs extends RemoveFromTuple<P, Extract<TBoundArgs["length"], number>>>(
|
||||||
|
thisArg: U,
|
||||||
|
...args: RemainingArgs
|
||||||
|
) => R extends [OrigThis, ...infer Rest]
|
||||||
|
? [U, ...ConcatTuples<TBoundArgs, Rest>] // Preserve bound args in return type
|
||||||
|
: R
|
||||||
|
: never;
|
||||||
|
|
||||||
|
declare function callBind<
|
||||||
|
const T extends (this: any, ...args: any[]) => any,
|
||||||
|
Extracted extends ExtractFunctionParams<T>,
|
||||||
|
const TBoundArgs extends Partial<Extracted["params"]> & readonly unknown[],
|
||||||
|
const TThis extends Extracted["thisArg"]
|
||||||
|
>(
|
||||||
|
args: [fn: T, thisArg: TThis, ...boundArgs: TBoundArgs]
|
||||||
|
): BindFunction<T, TThis, TBoundArgs, true>;
|
||||||
|
|
||||||
|
declare function callBind<
|
||||||
|
const T extends (this: any, ...args: any[]) => any,
|
||||||
|
Extracted extends ExtractFunctionParams<T>,
|
||||||
|
const TBoundArgs extends Partial<Extracted["params"]> & readonly unknown[]
|
||||||
|
>(
|
||||||
|
args: [fn: T, ...boundArgs: TBoundArgs]
|
||||||
|
): BindFunction<T, Extracted["thisArg"], TBoundArgs, false>;
|
||||||
|
|
||||||
|
declare function callBind<const TArgs extends readonly unknown[]>(
|
||||||
|
args: [fn: Exclude<TArgs[0], Function>, ...rest: TArgs]
|
||||||
|
): never;
|
||||||
|
|
||||||
|
// export as namespace callBind;
|
||||||
|
export = callBind;
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [ljharb]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: npm/es-errors
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with a single custom sponsorship URL
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* Manages the internal config of nodemon, checking for the state of support
|
||||||
|
* with fs.watch, how nodemon can watch files (using find or fs methods).
|
||||||
|
*
|
||||||
|
* This is *not* the user's config.
|
||||||
|
*/
|
||||||
|
var debug = require('debug')('nodemon');
|
||||||
|
var load = require('./load');
|
||||||
|
var rules = require('../rules');
|
||||||
|
var utils = require('../utils');
|
||||||
|
var pinVersion = require('../version').pin;
|
||||||
|
var command = require('./command');
|
||||||
|
var rulesToMonitor = require('../monitor/match').rulesToMonitor;
|
||||||
|
var bus = utils.bus;
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
rules.reset();
|
||||||
|
|
||||||
|
config.dirs = [];
|
||||||
|
config.options = { ignore: [], watch: [], monitor: [] };
|
||||||
|
config.lastStarted = 0;
|
||||||
|
config.loaded = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
run: false,
|
||||||
|
system: {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
},
|
||||||
|
required: false,
|
||||||
|
dirs: [],
|
||||||
|
timeout: 1000,
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take user defined settings, then detect the local machine capability, then
|
||||||
|
* look for local and global nodemon.json files and merge together the final
|
||||||
|
* settings with the config for nodemon.
|
||||||
|
*
|
||||||
|
* @param {Object} settings user defined settings for nodemon (typically on
|
||||||
|
* the cli)
|
||||||
|
* @param {Function} ready callback fired once the config is loaded
|
||||||
|
*/
|
||||||
|
config.load = function (settings, ready) {
|
||||||
|
reset();
|
||||||
|
var config = this;
|
||||||
|
load(settings, config.options, config, function (options) {
|
||||||
|
config.options = options;
|
||||||
|
|
||||||
|
if (options.watch.length === 0) {
|
||||||
|
// this is to catch when the watch is left blank
|
||||||
|
options.watch.push('*.*');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options['watch_interval']) { // jshint ignore:line
|
||||||
|
options.watchInterval = options['watch_interval']; // jshint ignore:line
|
||||||
|
}
|
||||||
|
|
||||||
|
config.watchInterval = options.watchInterval || null;
|
||||||
|
if (options.signal) {
|
||||||
|
config.signal = options.signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd = command(config.options);
|
||||||
|
config.command = {
|
||||||
|
raw: cmd,
|
||||||
|
string: utils.stringify(cmd.executable, cmd.args),
|
||||||
|
};
|
||||||
|
|
||||||
|
// now run automatic checks on system adding to the config object
|
||||||
|
options.monitor = rulesToMonitor(options.watch, options.ignore, config);
|
||||||
|
|
||||||
|
var cwd = process.cwd();
|
||||||
|
debug('config: dirs', config.dirs);
|
||||||
|
if (config.dirs.length === 0) {
|
||||||
|
config.dirs.unshift(cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.emit('config:update', config);
|
||||||
|
pinVersion().then(function () {
|
||||||
|
ready(config);
|
||||||
|
}).catch(e => {
|
||||||
|
// this doesn't help testing, but does give exposure on syntax errors
|
||||||
|
console.error(e.stack);
|
||||||
|
setTimeout(() => { throw e; }, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
config.reset = reset;
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||||
|
Copyright (c) 2016-2017 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var bind = require('function-bind');
|
||||||
|
var $TypeError = require('es-errors/type');
|
||||||
|
|
||||||
|
var $call = require('./functionCall');
|
||||||
|
var $actualApply = require('./actualApply');
|
||||||
|
|
||||||
|
/** @type {(args: [Function, thisArg?: unknown, ...args: unknown[]]) => Function} TODO FIXME, find a way to use import('.') */
|
||||||
|
module.exports = function callBindBasic(args) {
|
||||||
|
if (args.length < 1 || typeof args[0] !== 'function') {
|
||||||
|
throw new $TypeError('a function is required');
|
||||||
|
}
|
||||||
|
return $actualApply(bind, $call, args);
|
||||||
|
};
|
||||||
@ -0,0 +1,198 @@
|
|||||||
|
"use strict";
|
||||||
|
var Buffer = require("safer-buffer").Buffer;
|
||||||
|
|
||||||
|
// Export Node.js internal encodings.
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// Encodings
|
||||||
|
utf8: { type: "_internal", bomAware: true},
|
||||||
|
cesu8: { type: "_internal", bomAware: true},
|
||||||
|
unicode11utf8: "utf8",
|
||||||
|
|
||||||
|
ucs2: { type: "_internal", bomAware: true},
|
||||||
|
utf16le: "ucs2",
|
||||||
|
|
||||||
|
binary: { type: "_internal" },
|
||||||
|
base64: { type: "_internal" },
|
||||||
|
hex: { type: "_internal" },
|
||||||
|
|
||||||
|
// Codec.
|
||||||
|
_internal: InternalCodec,
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function InternalCodec(codecOptions, iconv) {
|
||||||
|
this.enc = codecOptions.encodingName;
|
||||||
|
this.bomAware = codecOptions.bomAware;
|
||||||
|
|
||||||
|
if (this.enc === "base64")
|
||||||
|
this.encoder = InternalEncoderBase64;
|
||||||
|
else if (this.enc === "cesu8") {
|
||||||
|
this.enc = "utf8"; // Use utf8 for decoding.
|
||||||
|
this.encoder = InternalEncoderCesu8;
|
||||||
|
|
||||||
|
// Add decoder for versions of Node not supporting CESU-8
|
||||||
|
if (Buffer.from('eda0bdedb2a9', 'hex').toString() !== '💩') {
|
||||||
|
this.decoder = InternalDecoderCesu8;
|
||||||
|
this.defaultCharUnicode = iconv.defaultCharUnicode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalCodec.prototype.encoder = InternalEncoder;
|
||||||
|
InternalCodec.prototype.decoder = InternalDecoder;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// We use node.js internal decoder. Its signature is the same as ours.
|
||||||
|
var StringDecoder = require('string_decoder').StringDecoder;
|
||||||
|
|
||||||
|
if (!StringDecoder.prototype.end) // Node v0.8 doesn't have this method.
|
||||||
|
StringDecoder.prototype.end = function() {};
|
||||||
|
|
||||||
|
|
||||||
|
function InternalDecoder(options, codec) {
|
||||||
|
this.decoder = new StringDecoder(codec.enc);
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalDecoder.prototype.write = function(buf) {
|
||||||
|
if (!Buffer.isBuffer(buf)) {
|
||||||
|
buf = Buffer.from(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.decoder.write(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalDecoder.prototype.end = function() {
|
||||||
|
return this.decoder.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Encoder is mostly trivial
|
||||||
|
|
||||||
|
function InternalEncoder(options, codec) {
|
||||||
|
this.enc = codec.enc;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalEncoder.prototype.write = function(str) {
|
||||||
|
return Buffer.from(str, this.enc);
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalEncoder.prototype.end = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Except base64 encoder, which must keep its state.
|
||||||
|
|
||||||
|
function InternalEncoderBase64(options, codec) {
|
||||||
|
this.prevStr = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalEncoderBase64.prototype.write = function(str) {
|
||||||
|
str = this.prevStr + str;
|
||||||
|
var completeQuads = str.length - (str.length % 4);
|
||||||
|
this.prevStr = str.slice(completeQuads);
|
||||||
|
str = str.slice(0, completeQuads);
|
||||||
|
|
||||||
|
return Buffer.from(str, "base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalEncoderBase64.prototype.end = function() {
|
||||||
|
return Buffer.from(this.prevStr, "base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// CESU-8 encoder is also special.
|
||||||
|
|
||||||
|
function InternalEncoderCesu8(options, codec) {
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalEncoderCesu8.prototype.write = function(str) {
|
||||||
|
var buf = Buffer.alloc(str.length * 3), bufIdx = 0;
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
var charCode = str.charCodeAt(i);
|
||||||
|
// Naive implementation, but it works because CESU-8 is especially easy
|
||||||
|
// to convert from UTF-16 (which all JS strings are encoded in).
|
||||||
|
if (charCode < 0x80)
|
||||||
|
buf[bufIdx++] = charCode;
|
||||||
|
else if (charCode < 0x800) {
|
||||||
|
buf[bufIdx++] = 0xC0 + (charCode >>> 6);
|
||||||
|
buf[bufIdx++] = 0x80 + (charCode & 0x3f);
|
||||||
|
}
|
||||||
|
else { // charCode will always be < 0x10000 in javascript.
|
||||||
|
buf[bufIdx++] = 0xE0 + (charCode >>> 12);
|
||||||
|
buf[bufIdx++] = 0x80 + ((charCode >>> 6) & 0x3f);
|
||||||
|
buf[bufIdx++] = 0x80 + (charCode & 0x3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.slice(0, bufIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalEncoderCesu8.prototype.end = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// CESU-8 decoder is not implemented in Node v4.0+
|
||||||
|
|
||||||
|
function InternalDecoderCesu8(options, codec) {
|
||||||
|
this.acc = 0;
|
||||||
|
this.contBytes = 0;
|
||||||
|
this.accBytes = 0;
|
||||||
|
this.defaultCharUnicode = codec.defaultCharUnicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalDecoderCesu8.prototype.write = function(buf) {
|
||||||
|
var acc = this.acc, contBytes = this.contBytes, accBytes = this.accBytes,
|
||||||
|
res = '';
|
||||||
|
for (var i = 0; i < buf.length; i++) {
|
||||||
|
var curByte = buf[i];
|
||||||
|
if ((curByte & 0xC0) !== 0x80) { // Leading byte
|
||||||
|
if (contBytes > 0) { // Previous code is invalid
|
||||||
|
res += this.defaultCharUnicode;
|
||||||
|
contBytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curByte < 0x80) { // Single-byte code
|
||||||
|
res += String.fromCharCode(curByte);
|
||||||
|
} else if (curByte < 0xE0) { // Two-byte code
|
||||||
|
acc = curByte & 0x1F;
|
||||||
|
contBytes = 1; accBytes = 1;
|
||||||
|
} else if (curByte < 0xF0) { // Three-byte code
|
||||||
|
acc = curByte & 0x0F;
|
||||||
|
contBytes = 2; accBytes = 1;
|
||||||
|
} else { // Four or more are not supported for CESU-8.
|
||||||
|
res += this.defaultCharUnicode;
|
||||||
|
}
|
||||||
|
} else { // Continuation byte
|
||||||
|
if (contBytes > 0) { // We're waiting for it.
|
||||||
|
acc = (acc << 6) | (curByte & 0x3f);
|
||||||
|
contBytes--; accBytes++;
|
||||||
|
if (contBytes === 0) {
|
||||||
|
// Check for overlong encoding, but support Modified UTF-8 (encoding NULL as C0 80)
|
||||||
|
if (accBytes === 2 && acc < 0x80 && acc > 0)
|
||||||
|
res += this.defaultCharUnicode;
|
||||||
|
else if (accBytes === 3 && acc < 0x800)
|
||||||
|
res += this.defaultCharUnicode;
|
||||||
|
else
|
||||||
|
// Actually add character.
|
||||||
|
res += String.fromCharCode(acc);
|
||||||
|
}
|
||||||
|
} else { // Unexpected continuation byte
|
||||||
|
res += this.defaultCharUnicode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.acc = acc; this.contBytes = contBytes; this.accBytes = accBytes;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalDecoderCesu8.prototype.end = function() {
|
||||||
|
var res = 0;
|
||||||
|
if (this.contBytes > 0)
|
||||||
|
res += this.defaultCharUnicode;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@ -0,0 +1,268 @@
|
|||||||
|
# Porting to the Buffer.from/Buffer.alloc API
|
||||||
|
|
||||||
|
<a id="overview"></a>
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
- [Variant 1: Drop support for Node.js ≤ 4.4.x and 5.0.0 — 5.9.x.](#variant-1) (*recommended*)
|
||||||
|
- [Variant 2: Use a polyfill](#variant-2)
|
||||||
|
- [Variant 3: manual detection, with safeguards](#variant-3)
|
||||||
|
|
||||||
|
### Finding problematic bits of code using grep
|
||||||
|
|
||||||
|
Just run `grep -nrE '[^a-zA-Z](Slow)?Buffer\s*\(' --exclude-dir node_modules`.
|
||||||
|
|
||||||
|
It will find all the potentially unsafe places in your own code (with some considerably unlikely
|
||||||
|
exceptions).
|
||||||
|
|
||||||
|
### Finding problematic bits of code using Node.js 8
|
||||||
|
|
||||||
|
If you’re using Node.js ≥ 8.0.0 (which is recommended), Node.js exposes multiple options that help with finding the relevant pieces of code:
|
||||||
|
|
||||||
|
- `--trace-warnings` will make Node.js show a stack trace for this warning and other warnings that are printed by Node.js.
|
||||||
|
- `--trace-deprecation` does the same thing, but only for deprecation warnings.
|
||||||
|
- `--pending-deprecation` will show more types of deprecation warnings. In particular, it will show the `Buffer()` deprecation warning, even on Node.js 8.
|
||||||
|
|
||||||
|
You can set these flags using an environment variable:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ export NODE_OPTIONS='--trace-warnings --pending-deprecation'
|
||||||
|
$ cat example.js
|
||||||
|
'use strict';
|
||||||
|
const foo = new Buffer('foo');
|
||||||
|
$ node example.js
|
||||||
|
(node:7147) [DEP0005] DeprecationWarning: The Buffer() and new Buffer() constructors are not recommended for use due to security and usability concerns. Please use the new Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() construction methods instead.
|
||||||
|
at showFlaggedDeprecation (buffer.js:127:13)
|
||||||
|
at new Buffer (buffer.js:148:3)
|
||||||
|
at Object.<anonymous> (/path/to/example.js:2:13)
|
||||||
|
[... more stack trace lines ...]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finding problematic bits of code using linters
|
||||||
|
|
||||||
|
Eslint rules [no-buffer-constructor](https://eslint.org/docs/rules/no-buffer-constructor)
|
||||||
|
or
|
||||||
|
[node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md)
|
||||||
|
also find calls to deprecated `Buffer()` API. Those rules are included in some pre-sets.
|
||||||
|
|
||||||
|
There is a drawback, though, that it doesn't always
|
||||||
|
[work correctly](https://github.com/chalker/safer-buffer#why-not-safe-buffer) when `Buffer` is
|
||||||
|
overriden e.g. with a polyfill, so recommended is a combination of this and some other method
|
||||||
|
described above.
|
||||||
|
|
||||||
|
<a id="variant-1"></a>
|
||||||
|
## Variant 1: Drop support for Node.js ≤ 4.4.x and 5.0.0 — 5.9.x.
|
||||||
|
|
||||||
|
This is the recommended solution nowadays that would imply only minimal overhead.
|
||||||
|
|
||||||
|
The Node.js 5.x release line has been unsupported since July 2016, and the Node.js 4.x release line reaches its End of Life in April 2018 (→ [Schedule](https://github.com/nodejs/Release#release-schedule)). This means that these versions of Node.js will *not* receive any updates, even in case of security issues, so using these release lines should be avoided, if at all possible.
|
||||||
|
|
||||||
|
What you would do in this case is to convert all `new Buffer()` or `Buffer()` calls to use `Buffer.alloc()` or `Buffer.from()`, in the following way:
|
||||||
|
|
||||||
|
- For `new Buffer(number)`, replace it with `Buffer.alloc(number)`.
|
||||||
|
- For `new Buffer(string)` (or `new Buffer(string, encoding)`), replace it with `Buffer.from(string)` (or `Buffer.from(string, encoding)`).
|
||||||
|
- For all other combinations of arguments (these are much rarer), also replace `new Buffer(...arguments)` with `Buffer.from(...arguments)`.
|
||||||
|
|
||||||
|
Note that `Buffer.alloc()` is also _faster_ on the current Node.js versions than
|
||||||
|
`new Buffer(size).fill(0)`, which is what you would otherwise need to ensure zero-filling.
|
||||||
|
|
||||||
|
Enabling eslint rule [no-buffer-constructor](https://eslint.org/docs/rules/no-buffer-constructor)
|
||||||
|
or
|
||||||
|
[node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md)
|
||||||
|
is recommended to avoid accidential unsafe Buffer API usage.
|
||||||
|
|
||||||
|
There is also a [JSCodeshift codemod](https://github.com/joyeecheung/node-dep-codemod#dep005)
|
||||||
|
for automatically migrating Buffer constructors to `Buffer.alloc()` or `Buffer.from()`.
|
||||||
|
Note that it currently only works with cases where the arguments are literals or where the
|
||||||
|
constructor is invoked with two arguments.
|
||||||
|
|
||||||
|
_If you currently support those older Node.js versions and dropping them would be a semver-major change
|
||||||
|
for you, or if you support older branches of your packages, consider using [Variant 2](#variant-2)
|
||||||
|
or [Variant 3](#variant-3) on older branches, so people using those older branches will also receive
|
||||||
|
the fix. That way, you will eradicate potential issues caused by unguarded Buffer API usage and
|
||||||
|
your users will not observe a runtime deprecation warning when running your code on Node.js 10._
|
||||||
|
|
||||||
|
<a id="variant-2"></a>
|
||||||
|
## Variant 2: Use a polyfill
|
||||||
|
|
||||||
|
Utilize [safer-buffer](https://www.npmjs.com/package/safer-buffer) as a polyfill to support older
|
||||||
|
Node.js versions.
|
||||||
|
|
||||||
|
You would take exacly the same steps as in [Variant 1](#variant-1), but with a polyfill
|
||||||
|
`const Buffer = require('safer-buffer').Buffer` in all files where you use the new `Buffer` api.
|
||||||
|
|
||||||
|
Make sure that you do not use old `new Buffer` API — in any files where the line above is added,
|
||||||
|
using old `new Buffer()` API will _throw_. It will be easy to notice that in CI, though.
|
||||||
|
|
||||||
|
Alternatively, you could use [buffer-from](https://www.npmjs.com/package/buffer-from) and/or
|
||||||
|
[buffer-alloc](https://www.npmjs.com/package/buffer-alloc) [ponyfills](https://ponyfill.com/) —
|
||||||
|
those are great, the only downsides being 4 deps in the tree and slightly more code changes to
|
||||||
|
migrate off them (as you would be using e.g. `Buffer.from` under a different name). If you need only
|
||||||
|
`Buffer.from` polyfilled — `buffer-from` alone which comes with no extra dependencies.
|
||||||
|
|
||||||
|
_Alternatively, you could use [safe-buffer](https://www.npmjs.com/package/safe-buffer) — it also
|
||||||
|
provides a polyfill, but takes a different approach which has
|
||||||
|
[it's drawbacks](https://github.com/chalker/safer-buffer#why-not-safe-buffer). It will allow you
|
||||||
|
to also use the older `new Buffer()` API in your code, though — but that's arguably a benefit, as
|
||||||
|
it is problematic, can cause issues in your code, and will start emitting runtime deprecation
|
||||||
|
warnings starting with Node.js 10._
|
||||||
|
|
||||||
|
Note that in either case, it is important that you also remove all calls to the old Buffer
|
||||||
|
API manually — just throwing in `safe-buffer` doesn't fix the problem by itself, it just provides
|
||||||
|
a polyfill for the new API. I have seen people doing that mistake.
|
||||||
|
|
||||||
|
Enabling eslint rule [no-buffer-constructor](https://eslint.org/docs/rules/no-buffer-constructor)
|
||||||
|
or
|
||||||
|
[node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md)
|
||||||
|
is recommended.
|
||||||
|
|
||||||
|
_Don't forget to drop the polyfill usage once you drop support for Node.js < 4.5.0._
|
||||||
|
|
||||||
|
<a id="variant-3"></a>
|
||||||
|
## Variant 3 — manual detection, with safeguards
|
||||||
|
|
||||||
|
This is useful if you create Buffer instances in only a few places (e.g. one), or you have your own
|
||||||
|
wrapper around them.
|
||||||
|
|
||||||
|
### Buffer(0)
|
||||||
|
|
||||||
|
This special case for creating empty buffers can be safely replaced with `Buffer.concat([])`, which
|
||||||
|
returns the same result all the way down to Node.js 0.8.x.
|
||||||
|
|
||||||
|
### Buffer(notNumber)
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var buf = new Buffer(notNumber, encoding);
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var buf;
|
||||||
|
if (Buffer.from && Buffer.from !== Uint8Array.from) {
|
||||||
|
buf = Buffer.from(notNumber, encoding);
|
||||||
|
} else {
|
||||||
|
if (typeof notNumber === 'number')
|
||||||
|
throw new Error('The "size" argument must be of type number.');
|
||||||
|
buf = new Buffer(notNumber, encoding);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`encoding` is optional.
|
||||||
|
|
||||||
|
Note that the `typeof notNumber` before `new Buffer` is required (for cases when `notNumber` argument is not
|
||||||
|
hard-coded) and _is not caused by the deprecation of Buffer constructor_ — it's exactly _why_ the
|
||||||
|
Buffer constructor is deprecated. Ecosystem packages lacking this type-check caused numereous
|
||||||
|
security issues — situations when unsanitized user input could end up in the `Buffer(arg)` create
|
||||||
|
problems ranging from DoS to leaking sensitive information to the attacker from the process memory.
|
||||||
|
|
||||||
|
When `notNumber` argument is hardcoded (e.g. literal `"abc"` or `[0,1,2]`), the `typeof` check can
|
||||||
|
be omitted.
|
||||||
|
|
||||||
|
Also note that using TypeScript does not fix this problem for you — when libs written in
|
||||||
|
`TypeScript` are used from JS, or when user input ends up there — it behaves exactly as pure JS, as
|
||||||
|
all type checks are translation-time only and are not present in the actual JS code which TS
|
||||||
|
compiles to.
|
||||||
|
|
||||||
|
### Buffer(number)
|
||||||
|
|
||||||
|
For Node.js 0.10.x (and below) support:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var buf;
|
||||||
|
if (Buffer.alloc) {
|
||||||
|
buf = Buffer.alloc(number);
|
||||||
|
} else {
|
||||||
|
buf = new Buffer(number);
|
||||||
|
buf.fill(0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise (Node.js ≥ 0.12.x):
|
||||||
|
|
||||||
|
```js
|
||||||
|
const buf = Buffer.alloc ? Buffer.alloc(number) : new Buffer(number).fill(0);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regarding Buffer.allocUnsafe
|
||||||
|
|
||||||
|
Be extra cautious when using `Buffer.allocUnsafe`:
|
||||||
|
* Don't use it if you don't have a good reason to
|
||||||
|
* e.g. you probably won't ever see a performance difference for small buffers, in fact, those
|
||||||
|
might be even faster with `Buffer.alloc()`,
|
||||||
|
* if your code is not in the hot code path — you also probably won't notice a difference,
|
||||||
|
* keep in mind that zero-filling minimizes the potential risks.
|
||||||
|
* If you use it, make sure that you never return the buffer in a partially-filled state,
|
||||||
|
* if you are writing to it sequentially — always truncate it to the actuall written length
|
||||||
|
|
||||||
|
Errors in handling buffers allocated with `Buffer.allocUnsafe` could result in various issues,
|
||||||
|
ranged from undefined behaviour of your code to sensitive data (user input, passwords, certs)
|
||||||
|
leaking to the remote attacker.
|
||||||
|
|
||||||
|
_Note that the same applies to `new Buffer` usage without zero-filling, depending on the Node.js
|
||||||
|
version (and lacking type checks also adds DoS to the list of potential problems)._
|
||||||
|
|
||||||
|
<a id="faq"></a>
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
<a id="design-flaws"></a>
|
||||||
|
### What is wrong with the `Buffer` constructor?
|
||||||
|
|
||||||
|
The `Buffer` constructor could be used to create a buffer in many different ways:
|
||||||
|
|
||||||
|
- `new Buffer(42)` creates a `Buffer` of 42 bytes. Before Node.js 8, this buffer contained
|
||||||
|
*arbitrary memory* for performance reasons, which could include anything ranging from
|
||||||
|
program source code to passwords and encryption keys.
|
||||||
|
- `new Buffer('abc')` creates a `Buffer` that contains the UTF-8-encoded version of
|
||||||
|
the string `'abc'`. A second argument could specify another encoding: For example,
|
||||||
|
`new Buffer(string, 'base64')` could be used to convert a Base64 string into the original
|
||||||
|
sequence of bytes that it represents.
|
||||||
|
- There are several other combinations of arguments.
|
||||||
|
|
||||||
|
This meant that, in code like `var buffer = new Buffer(foo);`, *it is not possible to tell
|
||||||
|
what exactly the contents of the generated buffer are* without knowing the type of `foo`.
|
||||||
|
|
||||||
|
Sometimes, the value of `foo` comes from an external source. For example, this function
|
||||||
|
could be exposed as a service on a web server, converting a UTF-8 string into its Base64 form:
|
||||||
|
|
||||||
|
```
|
||||||
|
function stringToBase64(req, res) {
|
||||||
|
// The request body should have the format of `{ string: 'foobar' }`
|
||||||
|
const rawBytes = new Buffer(req.body.string)
|
||||||
|
const encoded = rawBytes.toString('base64')
|
||||||
|
res.end({ encoded: encoded })
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this code does *not* validate the type of `req.body.string`:
|
||||||
|
|
||||||
|
- `req.body.string` is expected to be a string. If this is the case, all goes well.
|
||||||
|
- `req.body.string` is controlled by the client that sends the request.
|
||||||
|
- If `req.body.string` is the *number* `50`, the `rawBytes` would be 50 bytes:
|
||||||
|
- Before Node.js 8, the content would be uninitialized
|
||||||
|
- After Node.js 8, the content would be `50` bytes with the value `0`
|
||||||
|
|
||||||
|
Because of the missing type check, an attacker could intentionally send a number
|
||||||
|
as part of the request. Using this, they can either:
|
||||||
|
|
||||||
|
- Read uninitialized memory. This **will** leak passwords, encryption keys and other
|
||||||
|
kinds of sensitive information. (Information leak)
|
||||||
|
- Force the program to allocate a large amount of memory. For example, when specifying
|
||||||
|
`500000000` as the input value, each request will allocate 500MB of memory.
|
||||||
|
This can be used to either exhaust the memory available of a program completely
|
||||||
|
and make it crash, or slow it down significantly. (Denial of Service)
|
||||||
|
|
||||||
|
Both of these scenarios are considered serious security issues in a real-world
|
||||||
|
web server context.
|
||||||
|
|
||||||
|
when using `Buffer.from(req.body.string)` instead, passing a number will always
|
||||||
|
throw an exception instead, giving a controlled behaviour that can always be
|
||||||
|
handled by the program.
|
||||||
|
|
||||||
|
<a id="ecosystem-usage"></a>
|
||||||
|
### The `Buffer()` constructor has been deprecated for a while. Is this really an issue?
|
||||||
|
|
||||||
|
Surveys of code in the `npm` ecosystem have shown that the `Buffer()` constructor is still
|
||||||
|
widely used. This includes new code, and overall usage of such code has actually been
|
||||||
|
*increasing*.
|
||||||
@ -0,0 +1,212 @@
|
|||||||
|
# negotiator
|
||||||
|
|
||||||
|
[![NPM Version][npm-image]][npm-url]
|
||||||
|
[![NPM Downloads][downloads-image]][downloads-url]
|
||||||
|
[![Node.js Version][node-version-image]][node-version-url]
|
||||||
|
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
|
||||||
|
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||||
|
|
||||||
|
An HTTP content negotiator for Node.js
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ npm install negotiator
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
var Negotiator = require('negotiator')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Accept Negotiation
|
||||||
|
|
||||||
|
```js
|
||||||
|
availableMediaTypes = ['text/html', 'text/plain', 'application/json']
|
||||||
|
|
||||||
|
// The negotiator constructor receives a request object
|
||||||
|
negotiator = new Negotiator(request)
|
||||||
|
|
||||||
|
// Let's say Accept header is 'text/html, application/*;q=0.2, image/jpeg;q=0.8'
|
||||||
|
|
||||||
|
negotiator.mediaTypes()
|
||||||
|
// -> ['text/html', 'image/jpeg', 'application/*']
|
||||||
|
|
||||||
|
negotiator.mediaTypes(availableMediaTypes)
|
||||||
|
// -> ['text/html', 'application/json']
|
||||||
|
|
||||||
|
negotiator.mediaType(availableMediaTypes)
|
||||||
|
// -> 'text/html'
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check a working example at `examples/accept.js`.
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
|
||||||
|
##### mediaType()
|
||||||
|
|
||||||
|
Returns the most preferred media type from the client.
|
||||||
|
|
||||||
|
##### mediaType(availableMediaType)
|
||||||
|
|
||||||
|
Returns the most preferred media type from a list of available media types.
|
||||||
|
|
||||||
|
##### mediaTypes()
|
||||||
|
|
||||||
|
Returns an array of preferred media types ordered by the client preference.
|
||||||
|
|
||||||
|
##### mediaTypes(availableMediaTypes)
|
||||||
|
|
||||||
|
Returns an array of preferred media types ordered by priority from a list of
|
||||||
|
available media types.
|
||||||
|
|
||||||
|
### Accept-Language Negotiation
|
||||||
|
|
||||||
|
```js
|
||||||
|
negotiator = new Negotiator(request)
|
||||||
|
|
||||||
|
availableLanguages = ['en', 'es', 'fr']
|
||||||
|
|
||||||
|
// Let's say Accept-Language header is 'en;q=0.8, es, pt'
|
||||||
|
|
||||||
|
negotiator.languages()
|
||||||
|
// -> ['es', 'pt', 'en']
|
||||||
|
|
||||||
|
negotiator.languages(availableLanguages)
|
||||||
|
// -> ['es', 'en']
|
||||||
|
|
||||||
|
language = negotiator.language(availableLanguages)
|
||||||
|
// -> 'es'
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check a working example at `examples/language.js`.
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
|
||||||
|
##### language()
|
||||||
|
|
||||||
|
Returns the most preferred language from the client.
|
||||||
|
|
||||||
|
##### language(availableLanguages)
|
||||||
|
|
||||||
|
Returns the most preferred language from a list of available languages.
|
||||||
|
|
||||||
|
##### languages()
|
||||||
|
|
||||||
|
Returns an array of preferred languages ordered by the client preference.
|
||||||
|
|
||||||
|
##### languages(availableLanguages)
|
||||||
|
|
||||||
|
Returns an array of preferred languages ordered by priority from a list of
|
||||||
|
available languages.
|
||||||
|
|
||||||
|
### Accept-Charset Negotiation
|
||||||
|
|
||||||
|
```js
|
||||||
|
availableCharsets = ['utf-8', 'iso-8859-1', 'iso-8859-5']
|
||||||
|
|
||||||
|
negotiator = new Negotiator(request)
|
||||||
|
|
||||||
|
// Let's say Accept-Charset header is 'utf-8, iso-8859-1;q=0.8, utf-7;q=0.2'
|
||||||
|
|
||||||
|
negotiator.charsets()
|
||||||
|
// -> ['utf-8', 'iso-8859-1', 'utf-7']
|
||||||
|
|
||||||
|
negotiator.charsets(availableCharsets)
|
||||||
|
// -> ['utf-8', 'iso-8859-1']
|
||||||
|
|
||||||
|
negotiator.charset(availableCharsets)
|
||||||
|
// -> 'utf-8'
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check a working example at `examples/charset.js`.
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
|
||||||
|
##### charset()
|
||||||
|
|
||||||
|
Returns the most preferred charset from the client.
|
||||||
|
|
||||||
|
##### charset(availableCharsets)
|
||||||
|
|
||||||
|
Returns the most preferred charset from a list of available charsets.
|
||||||
|
|
||||||
|
##### charsets()
|
||||||
|
|
||||||
|
Returns an array of preferred charsets ordered by the client preference.
|
||||||
|
|
||||||
|
##### charsets(availableCharsets)
|
||||||
|
|
||||||
|
Returns an array of preferred charsets ordered by priority from a list of
|
||||||
|
available charsets.
|
||||||
|
|
||||||
|
### Accept-Encoding Negotiation
|
||||||
|
|
||||||
|
```js
|
||||||
|
availableEncodings = ['identity', 'gzip']
|
||||||
|
|
||||||
|
negotiator = new Negotiator(request)
|
||||||
|
|
||||||
|
// Let's say Accept-Encoding header is 'gzip, compress;q=0.2, identity;q=0.5'
|
||||||
|
|
||||||
|
negotiator.encodings()
|
||||||
|
// -> ['gzip', 'identity', 'compress']
|
||||||
|
|
||||||
|
negotiator.encodings(availableEncodings)
|
||||||
|
// -> ['gzip', 'identity']
|
||||||
|
|
||||||
|
negotiator.encoding(availableEncodings)
|
||||||
|
// -> 'gzip'
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check a working example at `examples/encoding.js`.
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
|
||||||
|
##### encoding()
|
||||||
|
|
||||||
|
Returns the most preferred encoding from the client.
|
||||||
|
|
||||||
|
##### encoding(availableEncodings)
|
||||||
|
|
||||||
|
Returns the most preferred encoding from a list of available encodings.
|
||||||
|
|
||||||
|
##### encoding(availableEncodings, { preferred })
|
||||||
|
|
||||||
|
Returns the most preferred encoding from a list of available encodings, while prioritizing based on `preferred` array between same-quality encodings.
|
||||||
|
|
||||||
|
##### encodings()
|
||||||
|
|
||||||
|
Returns an array of preferred encodings ordered by the client preference.
|
||||||
|
|
||||||
|
##### encodings(availableEncodings)
|
||||||
|
|
||||||
|
Returns an array of preferred encodings ordered by priority from a list of
|
||||||
|
available encodings.
|
||||||
|
|
||||||
|
##### encodings(availableEncodings, { preferred })
|
||||||
|
|
||||||
|
Returns an array of preferred encodings ordered by priority from a list of
|
||||||
|
available encodings, while prioritizing based on `preferred` array between same-quality encodings.
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
The [accepts](https://npmjs.org/package/accepts#readme) module builds on
|
||||||
|
this module and provides an alternative interface, mime type validation,
|
||||||
|
and more.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/negotiator.svg
|
||||||
|
[npm-url]: https://npmjs.org/package/negotiator
|
||||||
|
[node-version-image]: https://img.shields.io/node/v/negotiator.svg
|
||||||
|
[node-version-url]: https://nodejs.org/en/download/
|
||||||
|
[coveralls-image]: https://img.shields.io/coveralls/jshttp/negotiator/master.svg
|
||||||
|
[coveralls-url]: https://coveralls.io/r/jshttp/negotiator?branch=master
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/negotiator.svg
|
||||||
|
[downloads-url]: https://npmjs.org/package/negotiator
|
||||||
|
[github-actions-ci-image]: https://img.shields.io/github/workflow/status/jshttp/negotiator/ci/master?label=ci
|
||||||
|
[github-actions-ci-url]: https://github.com/jshttp/negotiator/actions/workflows/ci.yml
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
declare const MAX_ARRAY_LENGTH: 4294967295;
|
||||||
|
|
||||||
|
export = MAX_ARRAY_LENGTH;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const parse = require('./parse')
|
||||||
|
const prerelease = (version, options) => {
|
||||||
|
const parsed = parse(version, options)
|
||||||
|
return (parsed && parsed.prerelease.length) ? parsed.prerelease : null
|
||||||
|
}
|
||||||
|
module.exports = prerelease
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [ljharb]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: npm/gopd
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
@ -0,0 +1,216 @@
|
|||||||
|
# Path-to-RegExp
|
||||||
|
|
||||||
|
> Turn a path string such as `/user/:name` into a regular expression.
|
||||||
|
|
||||||
|
[![NPM version][npm-image]][npm-url]
|
||||||
|
[![NPM downloads][downloads-image]][downloads-url]
|
||||||
|
[![Build status][build-image]][build-url]
|
||||||
|
[![Build coverage][coverage-image]][coverage-url]
|
||||||
|
[![License][license-image]][license-url]
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install path-to-regexp --save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
const {
|
||||||
|
match,
|
||||||
|
pathToRegexp,
|
||||||
|
compile,
|
||||||
|
parse,
|
||||||
|
stringify,
|
||||||
|
} = require("path-to-regexp");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Parameters match arbitrary strings in a path by matching up to the end of the segment, or up to any proceeding tokens. They are defined by prefixing a colon to the parameter name (`:foo`). Parameter names can use any valid JavaScript identifier, or be double quoted to use other characters (`:"param-name"`).
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fn = match("/:foo/:bar");
|
||||||
|
|
||||||
|
fn("/test/route");
|
||||||
|
//=> { path: '/test/route', params: { foo: 'test', bar: 'route' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wildcard
|
||||||
|
|
||||||
|
Wildcard parameters match one or more characters across multiple segments. They are defined the same way as regular parameters, but are prefixed with an asterisk (`*foo`).
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fn = match("/*splat");
|
||||||
|
|
||||||
|
fn("/bar/baz");
|
||||||
|
//=> { path: '/bar/baz', params: { splat: [ 'bar', 'baz' ] } }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
Braces can be used to define parts of the path that are optional.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fn = match("/users{/:id}/delete");
|
||||||
|
|
||||||
|
fn("/users/delete");
|
||||||
|
//=> { path: '/users/delete', params: {} }
|
||||||
|
|
||||||
|
fn("/users/123/delete");
|
||||||
|
//=> { path: '/users/123/delete', params: { id: '123' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Match
|
||||||
|
|
||||||
|
The `match` function returns a function for matching strings against a path:
|
||||||
|
|
||||||
|
- **path** String or array of strings.
|
||||||
|
- **options** _(optional)_ (Extends [pathToRegexp](#pathToRegexp) options)
|
||||||
|
- **decode** Function for decoding strings to params, or `false` to disable all processing. (default: `decodeURIComponent`)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fn = match("/foo/:bar");
|
||||||
|
```
|
||||||
|
|
||||||
|
**Please note:** `path-to-regexp` is intended for ordered data (e.g. paths, hosts). It can not handle arbitrarily ordered data (e.g. query strings, URL fragments, JSON, etc).
|
||||||
|
|
||||||
|
## PathToRegexp
|
||||||
|
|
||||||
|
The `pathToRegexp` function returns a regular expression for matching strings against paths. It
|
||||||
|
|
||||||
|
- **path** String or array of strings.
|
||||||
|
- **options** _(optional)_ (See [parse](#parse) for more options)
|
||||||
|
- **sensitive** Regexp will be case sensitive. (default: `false`)
|
||||||
|
- **end** Validate the match reaches the end of the string. (default: `true`)
|
||||||
|
- **delimiter** The default delimiter for segments, e.g. `[^/]` for `:named` parameters. (default: `'/'`)
|
||||||
|
- **trailing** Allows optional trailing delimiter to match. (default: `true`)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { regexp, keys } = pathToRegexp("/foo/:bar");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compile ("Reverse" Path-To-RegExp)
|
||||||
|
|
||||||
|
The `compile` function will return a function for transforming parameters into a valid path:
|
||||||
|
|
||||||
|
- **path** A string.
|
||||||
|
- **options** (See [parse](#parse) for more options)
|
||||||
|
- **delimiter** The default delimiter for segments, e.g. `[^/]` for `:named` parameters. (default: `'/'`)
|
||||||
|
- **encode** Function for encoding input strings for output into the path, or `false` to disable entirely. (default: `encodeURIComponent`)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const toPath = compile("/user/:id");
|
||||||
|
|
||||||
|
toPath({ id: "name" }); //=> "/user/name"
|
||||||
|
toPath({ id: "café" }); //=> "/user/caf%C3%A9"
|
||||||
|
|
||||||
|
const toPathRepeated = compile("/*segment");
|
||||||
|
|
||||||
|
toPathRepeated({ segment: ["foo"] }); //=> "/foo"
|
||||||
|
toPathRepeated({ segment: ["a", "b", "c"] }); //=> "/a/b/c"
|
||||||
|
|
||||||
|
// When disabling `encode`, you need to make sure inputs are encoded correctly. No arrays are accepted.
|
||||||
|
const toPathRaw = compile("/user/:id", { encode: false });
|
||||||
|
|
||||||
|
toPathRaw({ id: "%3A%2F" }); //=> "/user/%3A%2F"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stringify
|
||||||
|
|
||||||
|
Transform `TokenData` (a sequence of tokens) back into a Path-to-RegExp string.
|
||||||
|
|
||||||
|
- **data** A `TokenData` instance
|
||||||
|
|
||||||
|
```js
|
||||||
|
const data = new TokenData([
|
||||||
|
{ type: "text", value: "/" },
|
||||||
|
{ type: "param", name: "foo" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const path = stringify(data); //=> "/:foo"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Developers
|
||||||
|
|
||||||
|
- If you are rewriting paths with match and compile, consider using `encode: false` and `decode: false` to keep raw paths passed around.
|
||||||
|
- To ensure matches work on paths containing characters usually encoded, such as emoji, consider using [encodeurl](https://github.com/pillarjs/encodeurl) for `encodePath`.
|
||||||
|
|
||||||
|
### Parse
|
||||||
|
|
||||||
|
The `parse` function accepts a string and returns `TokenData`, the set of tokens and other metadata parsed from the input string. `TokenData` is can used with `match` and `compile`.
|
||||||
|
|
||||||
|
- **path** A string.
|
||||||
|
- **options** _(optional)_
|
||||||
|
- **encodePath** A function for encoding input strings. (default: `x => x`, recommended: [`encodeurl`](https://github.com/pillarjs/encodeurl))
|
||||||
|
|
||||||
|
### Tokens
|
||||||
|
|
||||||
|
`TokenData` is a sequence of tokens, currently of types `text`, `parameter`, `wildcard`, or `group`.
|
||||||
|
|
||||||
|
### Custom path
|
||||||
|
|
||||||
|
In some applications, you may not be able to use the `path-to-regexp` syntax, but still want to use this library for `match` and `compile`. For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { TokenData, match } from "path-to-regexp";
|
||||||
|
|
||||||
|
const tokens = [
|
||||||
|
{ type: "text", value: "/" },
|
||||||
|
{ type: "parameter", name: "foo" },
|
||||||
|
];
|
||||||
|
const path = new TokenData(tokens);
|
||||||
|
const fn = match(path);
|
||||||
|
|
||||||
|
fn("/test"); //=> { path: '/test', index: 0, params: { foo: 'test' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
An effort has been made to ensure ambiguous paths from previous releases throw an error. This means you might be seeing an error when things worked before.
|
||||||
|
|
||||||
|
### Unexpected `?` or `+`
|
||||||
|
|
||||||
|
In past releases, `?`, `*`, and `+` were used to denote optional or repeating parameters. As an alternative, try these:
|
||||||
|
|
||||||
|
- For optional (`?`), use an empty segment in a group such as `/:file{.:ext}`.
|
||||||
|
- For repeating (`+`), only wildcard matching is supported, such as `/*path`.
|
||||||
|
- For optional repeating (`*`), use a group and a wildcard parameter such as `/files{/*path}`.
|
||||||
|
|
||||||
|
### Unexpected `(`, `)`, `[`, `]`, etc.
|
||||||
|
|
||||||
|
Previous versions of Path-to-RegExp used these for RegExp features. This version no longer supports them so they've been reserved to avoid ambiguity. To use these characters literally, escape them with a backslash, e.g. `"\\("`.
|
||||||
|
|
||||||
|
### Missing parameter name
|
||||||
|
|
||||||
|
Parameter names must be provided after `:` or `*`, and they must be a valid JavaScript identifier. If you want an parameter name that isn't a JavaScript identifier, such as starting with a number, you can wrap the name in quotes like `:"my-name"`.
|
||||||
|
|
||||||
|
### Unterminated quote
|
||||||
|
|
||||||
|
Parameter names can be wrapped in double quote characters, and this error means you forgot to close the quote character.
|
||||||
|
|
||||||
|
### Express <= 4.x
|
||||||
|
|
||||||
|
Path-To-RegExp breaks compatibility with Express <= `4.x` in the following ways:
|
||||||
|
|
||||||
|
- The wildcard `*` must have a name, matching the behavior of parameters `:`.
|
||||||
|
- The optional character `?` is no longer supported, use braces instead: `/:file{.:ext}`.
|
||||||
|
- Regexp characters are not supported.
|
||||||
|
- Some characters have been reserved to avoid confusion during upgrade (`()[]?+!`).
|
||||||
|
- Parameter names now support valid JavaScript identifiers, or quoted like `:"this"`.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/path-to-regexp
|
||||||
|
[npm-url]: https://npmjs.org/package/path-to-regexp
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/path-to-regexp
|
||||||
|
[downloads-url]: https://npmjs.org/package/path-to-regexp
|
||||||
|
[build-image]: https://img.shields.io/github/actions/workflow/status/pillarjs/path-to-regexp/ci.yml?branch=master
|
||||||
|
[build-url]: https://github.com/pillarjs/path-to-regexp/actions/workflows/ci.yml?query=branch%3Amaster
|
||||||
|
[coverage-image]: https://img.shields.io/codecov/c/gh/pillarjs/path-to-regexp
|
||||||
|
[coverage-url]: https://codecov.io/gh/pillarjs/path-to-regexp
|
||||||
|
[license-image]: http://img.shields.io/npm/l/path-to-regexp.svg?style=flat
|
||||||
|
[license-url]: LICENSE.md
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,76 @@
|
|||||||
|
var inspect = require('../');
|
||||||
|
var test = require('tape');
|
||||||
|
var arrow = require('make-arrow-function')();
|
||||||
|
var functionsHaveConfigurableNames = require('functions-have-names').functionsHaveConfigurableNames();
|
||||||
|
|
||||||
|
test('function', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
var obj = [1, 2, function f(n) { return n; }, 4];
|
||||||
|
t.equal(inspect(obj), '[ 1, 2, [Function: f], 4 ]');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('function name', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
var f = (function () {
|
||||||
|
return function () {};
|
||||||
|
}());
|
||||||
|
f.toString = function toStr() { return 'function xxx () {}'; };
|
||||||
|
var obj = [1, 2, f, 4];
|
||||||
|
t.equal(inspect(obj), '[ 1, 2, [Function (anonymous)] { toString: [Function: toStr] }, 4 ]');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('anon function', function (t) {
|
||||||
|
var f = (function () {
|
||||||
|
return function () {};
|
||||||
|
}());
|
||||||
|
var obj = [1, 2, f, 4];
|
||||||
|
t.equal(inspect(obj), '[ 1, 2, [Function (anonymous)], 4 ]');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('arrow function', { skip: !arrow }, function (t) {
|
||||||
|
t.equal(inspect(arrow), '[Function (anonymous)]');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('truly nameless function', { skip: !arrow || !functionsHaveConfigurableNames }, function (t) {
|
||||||
|
function f() {}
|
||||||
|
Object.defineProperty(f, 'name', { value: false });
|
||||||
|
t.equal(f.name, false);
|
||||||
|
t.equal(
|
||||||
|
inspect(f),
|
||||||
|
'[Function: f]',
|
||||||
|
'named function with falsy `.name` does not hide its original name'
|
||||||
|
);
|
||||||
|
|
||||||
|
function g() {}
|
||||||
|
Object.defineProperty(g, 'name', { value: true });
|
||||||
|
t.equal(g.name, true);
|
||||||
|
t.equal(
|
||||||
|
inspect(g),
|
||||||
|
'[Function: true]',
|
||||||
|
'named function with truthy `.name` hides its original name'
|
||||||
|
);
|
||||||
|
|
||||||
|
var anon = function () {}; // eslint-disable-line func-style
|
||||||
|
Object.defineProperty(anon, 'name', { value: null });
|
||||||
|
t.equal(anon.name, null);
|
||||||
|
t.equal(
|
||||||
|
inspect(anon),
|
||||||
|
'[Function (anonymous)]',
|
||||||
|
'anon function with falsy `.name` does not hide its anonymity'
|
||||||
|
);
|
||||||
|
|
||||||
|
var anon2 = function () {}; // eslint-disable-line func-style
|
||||||
|
Object.defineProperty(anon2, 'name', { value: 1 });
|
||||||
|
t.equal(anon2.name, 1);
|
||||||
|
t.equal(
|
||||||
|
inspect(anon2),
|
||||||
|
'[Function: 1]',
|
||||||
|
'anon function with truthy `.name` hides its anonymity'
|
||||||
|
);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
The ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2015, 2019 Elan Shanker
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||||
|
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
/*!
|
||||||
|
* encodeurl
|
||||||
|
* Copyright(c) 2016 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = encodeUrl
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to match non-URL code points, *after* encoding (i.e. not including "%")
|
||||||
|
* and including invalid escape sequences.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var ENCODE_CHARS_REGEXP = /(?:[^\x21\x23-\x3B\x3D\x3F-\x5F\x61-\x7A\x7C\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to match unmatched surrogate pair.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var UNMATCHED_SURROGATE_PAIR_REGEXP = /(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String to replace unmatched surrogate pair with.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var UNMATCHED_SURROGATE_PAIR_REPLACE = '$1\uFFFD$2'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a URL to a percent-encoded form, excluding already-encoded sequences.
|
||||||
|
*
|
||||||
|
* This function will take an already-encoded URL and encode all the non-URL
|
||||||
|
* code points. This function will not encode the "%" character unless it is
|
||||||
|
* not part of a valid sequence (`%20` will be left as-is, but `%foo` will
|
||||||
|
* be encoded as `%25foo`).
|
||||||
|
*
|
||||||
|
* This encode is meant to be "safe" and does not throw errors. It will try as
|
||||||
|
* hard as it can to properly encode the given URL, including replacing any raw,
|
||||||
|
* unpaired surrogate pairs with the Unicode replacement character prior to
|
||||||
|
* encoding.
|
||||||
|
*
|
||||||
|
* @param {string} url
|
||||||
|
* @return {string}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function encodeUrl (url) {
|
||||||
|
return String(url)
|
||||||
|
.replace(UNMATCHED_SURROGATE_PAIR_REGEXP, UNMATCHED_SURROGATE_PAIR_REPLACE)
|
||||||
|
.replace(ENCODE_CHARS_REGEXP, encodeURI)
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var bind = require('function-bind');
|
||||||
|
var $apply = require('./functionApply');
|
||||||
|
var actualApply = require('./actualApply');
|
||||||
|
|
||||||
|
/** @type {import('./applyBind')} */
|
||||||
|
module.exports = function applyBind() {
|
||||||
|
return actualApply(bind, $apply, arguments);
|
||||||
|
};
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v1.0.2](https://github.com/ljharb/call-bind-apply-helpers/compare/v1.0.1...v1.0.2) - 2025-02-12
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [types] improve inferred types [`e6f9586`](https://github.com/ljharb/call-bind-apply-helpers/commit/e6f95860a3c72879cb861a858cdfb8138fbedec1)
|
||||||
|
- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/tsconfig`, `@types/tape`, `es-value-fixtures`, `for-each`, `has-strict-mode`, `object-inspect` [`e43d540`](https://github.com/ljharb/call-bind-apply-helpers/commit/e43d5409f97543bfbb11f345d47d8ce4e066d8c1)
|
||||||
|
|
||||||
|
## [v1.0.1](https://github.com/ljharb/call-bind-apply-helpers/compare/v1.0.0...v1.0.1) - 2024-12-08
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [types] `reflectApply`: fix types [`4efc396`](https://github.com/ljharb/call-bind-apply-helpers/commit/4efc3965351a4f02cc55e836fa391d3d11ef2ef8)
|
||||||
|
- [Fix] `reflectApply`: oops, Reflect is not a function [`83cc739`](https://github.com/ljharb/call-bind-apply-helpers/commit/83cc7395de6b79b7730bdf092f1436f0b1263c75)
|
||||||
|
- [Dev Deps] update `@arethetypeswrong/cli` [`80bd5d3`](https://github.com/ljharb/call-bind-apply-helpers/commit/80bd5d3ae58b4f6b6995ce439dd5a1bcb178a940)
|
||||||
|
|
||||||
|
## v1.0.0 - 2024-12-05
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- Initial implementation, tests, readme [`7879629`](https://github.com/ljharb/call-bind-apply-helpers/commit/78796290f9b7430c9934d6f33d94ae9bc89fce04)
|
||||||
|
- Initial commit [`3f1dc16`](https://github.com/ljharb/call-bind-apply-helpers/commit/3f1dc164afc43285631b114a5f9dd9137b2b952f)
|
||||||
|
- npm init [`081df04`](https://github.com/ljharb/call-bind-apply-helpers/commit/081df048c312fcee400922026f6e97281200a603)
|
||||||
|
- Only apps should have lockfiles [`5b9ca0f`](https://github.com/ljharb/call-bind-apply-helpers/commit/5b9ca0fe8101ebfaf309c549caac4e0a017ed930)
|
||||||
@ -0,0 +1,180 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Buffer = require("safer-buffer").Buffer;
|
||||||
|
|
||||||
|
var bomHandling = require("./bom-handling"),
|
||||||
|
iconv = module.exports;
|
||||||
|
|
||||||
|
// All codecs and aliases are kept here, keyed by encoding name/alias.
|
||||||
|
// They are lazy loaded in `iconv.getCodec` from `encodings/index.js`.
|
||||||
|
iconv.encodings = null;
|
||||||
|
|
||||||
|
// Characters emitted in case of error.
|
||||||
|
iconv.defaultCharUnicode = '<27>';
|
||||||
|
iconv.defaultCharSingleByte = '?';
|
||||||
|
|
||||||
|
// Public API.
|
||||||
|
iconv.encode = function encode(str, encoding, options) {
|
||||||
|
str = "" + (str || ""); // Ensure string.
|
||||||
|
|
||||||
|
var encoder = iconv.getEncoder(encoding, options);
|
||||||
|
|
||||||
|
var res = encoder.write(str);
|
||||||
|
var trail = encoder.end();
|
||||||
|
|
||||||
|
return (trail && trail.length > 0) ? Buffer.concat([res, trail]) : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconv.decode = function decode(buf, encoding, options) {
|
||||||
|
if (typeof buf === 'string') {
|
||||||
|
if (!iconv.skipDecodeWarning) {
|
||||||
|
console.error('Iconv-lite warning: decode()-ing strings is deprecated. Refer to https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding');
|
||||||
|
iconv.skipDecodeWarning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = Buffer.from("" + (buf || ""), "binary"); // Ensure buffer.
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoder = iconv.getDecoder(encoding, options);
|
||||||
|
|
||||||
|
var res = decoder.write(buf);
|
||||||
|
var trail = decoder.end();
|
||||||
|
|
||||||
|
return trail ? (res + trail) : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconv.encodingExists = function encodingExists(enc) {
|
||||||
|
try {
|
||||||
|
iconv.getCodec(enc);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy aliases to convert functions
|
||||||
|
iconv.toEncoding = iconv.encode;
|
||||||
|
iconv.fromEncoding = iconv.decode;
|
||||||
|
|
||||||
|
// Search for a codec in iconv.encodings. Cache codec data in iconv._codecDataCache.
|
||||||
|
iconv._codecDataCache = {};
|
||||||
|
iconv.getCodec = function getCodec(encoding) {
|
||||||
|
if (!iconv.encodings)
|
||||||
|
iconv.encodings = require("../encodings"); // Lazy load all encoding definitions.
|
||||||
|
|
||||||
|
// Canonicalize encoding name: strip all non-alphanumeric chars and appended year.
|
||||||
|
var enc = iconv._canonicalizeEncoding(encoding);
|
||||||
|
|
||||||
|
// Traverse iconv.encodings to find actual codec.
|
||||||
|
var codecOptions = {};
|
||||||
|
while (true) {
|
||||||
|
var codec = iconv._codecDataCache[enc];
|
||||||
|
if (codec)
|
||||||
|
return codec;
|
||||||
|
|
||||||
|
var codecDef = iconv.encodings[enc];
|
||||||
|
|
||||||
|
switch (typeof codecDef) {
|
||||||
|
case "string": // Direct alias to other encoding.
|
||||||
|
enc = codecDef;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "object": // Alias with options. Can be layered.
|
||||||
|
for (var key in codecDef)
|
||||||
|
codecOptions[key] = codecDef[key];
|
||||||
|
|
||||||
|
if (!codecOptions.encodingName)
|
||||||
|
codecOptions.encodingName = enc;
|
||||||
|
|
||||||
|
enc = codecDef.type;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "function": // Codec itself.
|
||||||
|
if (!codecOptions.encodingName)
|
||||||
|
codecOptions.encodingName = enc;
|
||||||
|
|
||||||
|
// The codec function must load all tables and return object with .encoder and .decoder methods.
|
||||||
|
// It'll be called only once (for each different options object).
|
||||||
|
codec = new codecDef(codecOptions, iconv);
|
||||||
|
|
||||||
|
iconv._codecDataCache[codecOptions.encodingName] = codec; // Save it to be reused later.
|
||||||
|
return codec;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("Encoding not recognized: '" + encoding + "' (searched as: '"+enc+"')");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iconv._canonicalizeEncoding = function(encoding) {
|
||||||
|
// Canonicalize encoding name: strip all non-alphanumeric chars and appended year.
|
||||||
|
return (''+encoding).toLowerCase().replace(/:\d{4}$|[^0-9a-z]/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
iconv.getEncoder = function getEncoder(encoding, options) {
|
||||||
|
var codec = iconv.getCodec(encoding),
|
||||||
|
encoder = new codec.encoder(options, codec);
|
||||||
|
|
||||||
|
if (codec.bomAware && options && options.addBOM)
|
||||||
|
encoder = new bomHandling.PrependBOM(encoder, options);
|
||||||
|
|
||||||
|
return encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconv.getDecoder = function getDecoder(encoding, options) {
|
||||||
|
var codec = iconv.getCodec(encoding),
|
||||||
|
decoder = new codec.decoder(options, codec);
|
||||||
|
|
||||||
|
if (codec.bomAware && !(options && options.stripBOM === false))
|
||||||
|
decoder = new bomHandling.StripBOM(decoder, options);
|
||||||
|
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming API
|
||||||
|
// NOTE: Streaming API naturally depends on 'stream' module from Node.js. Unfortunately in browser environments this module can add
|
||||||
|
// up to 100Kb to the output bundle. To avoid unnecessary code bloat, we don't enable Streaming API in browser by default.
|
||||||
|
// If you would like to enable it explicitly, please add the following code to your app:
|
||||||
|
// > iconv.enableStreamingAPI(require('stream'));
|
||||||
|
iconv.enableStreamingAPI = function enableStreamingAPI(stream_module) {
|
||||||
|
if (iconv.supportsStreams)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Dependency-inject stream module to create IconvLite stream classes.
|
||||||
|
var streams = require("./streams")(stream_module);
|
||||||
|
|
||||||
|
// Not public API yet, but expose the stream classes.
|
||||||
|
iconv.IconvLiteEncoderStream = streams.IconvLiteEncoderStream;
|
||||||
|
iconv.IconvLiteDecoderStream = streams.IconvLiteDecoderStream;
|
||||||
|
|
||||||
|
// Streaming API.
|
||||||
|
iconv.encodeStream = function encodeStream(encoding, options) {
|
||||||
|
return new iconv.IconvLiteEncoderStream(iconv.getEncoder(encoding, options), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
iconv.decodeStream = function decodeStream(encoding, options) {
|
||||||
|
return new iconv.IconvLiteDecoderStream(iconv.getDecoder(encoding, options), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
iconv.supportsStreams = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable Streaming API automatically if 'stream' module is available and non-empty (the majority of environments).
|
||||||
|
var stream_module;
|
||||||
|
try {
|
||||||
|
stream_module = require("stream");
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (stream_module && stream_module.Transform) {
|
||||||
|
iconv.enableStreamingAPI(stream_module);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// In rare cases where 'stream' module is not available by default, throw a helpful exception.
|
||||||
|
iconv.encodeStream = iconv.decodeStream = function() {
|
||||||
|
throw new Error("iconv-lite Streaming API is not enabled. Use iconv.enableStreamingAPI(require('stream')); to enable it.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("Ā" != "\u0100") {
|
||||||
|
console.error("iconv-lite warning: js files use non-utf8 encoding. See https://github.com/ashtuchkin/iconv-lite/wiki/Javascript-source-file-encodings for more info.");
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"name": "fill-range",
|
||||||
|
"description": "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`",
|
||||||
|
"version": "7.1.1",
|
||||||
|
"homepage": "https://github.com/jonschlinkert/fill-range",
|
||||||
|
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
|
||||||
|
"contributors": [
|
||||||
|
"Edo Rivai (edo.rivai.nl)",
|
||||||
|
"Jon Schlinkert (http://twitter.com/jonschlinkert)",
|
||||||
|
"Paul Miller (paulmillr.com)",
|
||||||
|
"Rouven Weßling (www.rouvenwessling.de)",
|
||||||
|
"(https://github.com/wtgtybhertgeghgtwtg)"
|
||||||
|
],
|
||||||
|
"repository": "jonschlinkert/fill-range",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/jonschlinkert/fill-range/issues"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"files": [
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"main": "index.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint --cache --cache-location node_modules/.cache/.eslintcache --report-unused-disable-directives --ignore-path .gitignore .",
|
||||||
|
"mocha": "mocha --reporter dot",
|
||||||
|
"test": "npm run lint && npm run mocha",
|
||||||
|
"test:ci": "npm run test:cover",
|
||||||
|
"test:cover": "nyc npm run mocha"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp-format-md": "^2.0.0",
|
||||||
|
"mocha": "^6.1.1",
|
||||||
|
"nyc": "^15.1.0"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"alpha",
|
||||||
|
"alphabetical",
|
||||||
|
"array",
|
||||||
|
"bash",
|
||||||
|
"brace",
|
||||||
|
"expand",
|
||||||
|
"expansion",
|
||||||
|
"fill",
|
||||||
|
"glob",
|
||||||
|
"match",
|
||||||
|
"matches",
|
||||||
|
"matching",
|
||||||
|
"number",
|
||||||
|
"numerical",
|
||||||
|
"range",
|
||||||
|
"ranges",
|
||||||
|
"regex",
|
||||||
|
"sh"
|
||||||
|
],
|
||||||
|
"verb": {
|
||||||
|
"toc": false,
|
||||||
|
"layout": "default",
|
||||||
|
"tasks": [
|
||||||
|
"readme"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"gulp-format-md"
|
||||||
|
],
|
||||||
|
"lint": {
|
||||||
|
"reflinks": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
exports.isInteger = num => {
|
||||||
|
if (typeof num === 'number') {
|
||||||
|
return Number.isInteger(num);
|
||||||
|
}
|
||||||
|
if (typeof num === 'string' && num.trim() !== '') {
|
||||||
|
return Number.isInteger(Number(num));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a node of the given type
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.find = (node, type) => node.nodes.find(node => node.type === type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a node of the given type
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.exceedsLimit = (min, max, step = 1, limit) => {
|
||||||
|
if (limit === false) return false;
|
||||||
|
if (!exports.isInteger(min) || !exports.isInteger(max)) return false;
|
||||||
|
return ((Number(max) - Number(min)) / Number(step)) >= limit;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape the given node with '\\' before node.value
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.escapeNode = (block, n = 0, type) => {
|
||||||
|
const node = block.nodes[n];
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
if ((type && node.type === type) || node.type === 'open' || node.type === 'close') {
|
||||||
|
if (node.escaped !== true) {
|
||||||
|
node.value = '\\' + node.value;
|
||||||
|
node.escaped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given brace node should be enclosed in literal braces
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.encloseBrace = node => {
|
||||||
|
if (node.type !== 'brace') return false;
|
||||||
|
if ((node.commas >> 0 + node.ranges >> 0) === 0) {
|
||||||
|
node.invalid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a brace node is invalid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.isInvalidBrace = block => {
|
||||||
|
if (block.type !== 'brace') return false;
|
||||||
|
if (block.invalid === true || block.dollar) return true;
|
||||||
|
if ((block.commas >> 0 + block.ranges >> 0) === 0) {
|
||||||
|
block.invalid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (block.open !== true || block.close !== true) {
|
||||||
|
block.invalid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a node is an open or close node
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.isOpenOrClose = node => {
|
||||||
|
if (node.type === 'open' || node.type === 'close') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return node.open === true || node.close === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce an array of text nodes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.reduce = nodes => nodes.reduce((acc, node) => {
|
||||||
|
if (node.type === 'text') acc.push(node.value);
|
||||||
|
if (node.type === 'range') node.type = 'text';
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten an array
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.flatten = (...args) => {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
const flat = arr => {
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
const ele = arr[i];
|
||||||
|
|
||||||
|
if (Array.isArray(ele)) {
|
||||||
|
flat(ele);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ele !== undefined) {
|
||||||
|
result.push(ele);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
flat(args);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Store
|
||||||
|
*/
|
||||||
|
|
||||||
|
var evt = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On
|
||||||
|
*
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Function} fn
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
on: function(key, fn) {
|
||||||
|
evt[key] || (evt[key] = []);
|
||||||
|
evt[key].push(fn);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Off
|
||||||
|
*
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Function} fn
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
off: function(key, fn) {
|
||||||
|
var val = evt[key];
|
||||||
|
|
||||||
|
if(!val) return;
|
||||||
|
|
||||||
|
var i = val.indexOf(fn);
|
||||||
|
|
||||||
|
if(-1 == i) return;
|
||||||
|
|
||||||
|
val.splice(i, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One
|
||||||
|
*
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Mixed} val
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
one: function(key, val) {
|
||||||
|
var ref = evt[key];
|
||||||
|
|
||||||
|
if(!ref || !ref.length) return;
|
||||||
|
|
||||||
|
ref[0](val);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit
|
||||||
|
*
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Mixed} val
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
emit: function(key, val) {
|
||||||
|
if(!evt[key]) return;
|
||||||
|
|
||||||
|
evt[key].forEach(emit);
|
||||||
|
|
||||||
|
function emit(fn) {
|
||||||
|
fn(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Range = require('../classes/range')
|
||||||
|
|
||||||
|
// Mostly just for testing and legacy API reasons
|
||||||
|
const toComparators = (range, options) =>
|
||||||
|
new Range(range, options).set
|
||||||
|
.map(comp => comp.map(c => c.value).join(' ').trim().split(' '))
|
||||||
|
|
||||||
|
module.exports = toComparators
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
// given a set of versions and a range, create a "simplified" range
|
||||||
|
// that includes the same versions that the original range does
|
||||||
|
// If the original range is shorter than the simplified one, return that.
|
||||||
|
const satisfies = require('../functions/satisfies.js')
|
||||||
|
const compare = require('../functions/compare.js')
|
||||||
|
module.exports = (versions, range, options) => {
|
||||||
|
const set = []
|
||||||
|
let first = null
|
||||||
|
let prev = null
|
||||||
|
const v = versions.sort((a, b) => compare(a, b, options))
|
||||||
|
for (const version of v) {
|
||||||
|
const included = satisfies(version, range, options)
|
||||||
|
if (included) {
|
||||||
|
prev = version
|
||||||
|
if (!first) {
|
||||||
|
first = version
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (prev) {
|
||||||
|
set.push([first, prev])
|
||||||
|
}
|
||||||
|
prev = null
|
||||||
|
first = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (first) {
|
||||||
|
set.push([first, null])
|
||||||
|
}
|
||||||
|
|
||||||
|
const ranges = []
|
||||||
|
for (const [min, max] of set) {
|
||||||
|
if (min === max) {
|
||||||
|
ranges.push(min)
|
||||||
|
} else if (!max && min === v[0]) {
|
||||||
|
ranges.push('*')
|
||||||
|
} else if (!max) {
|
||||||
|
ranges.push(`>=${min}`)
|
||||||
|
} else if (min === v[0]) {
|
||||||
|
ranges.push(`<=${max}`)
|
||||||
|
} else {
|
||||||
|
ranges.push(`${min} - ${max}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const simplified = ranges.join(' || ')
|
||||||
|
const original = typeof range.raw === 'string' ? range.raw : String(range)
|
||||||
|
return simplified.length < original.length ? simplified : range
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2013 Raynos.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
declare function setPrototypeOf(o: any, proto: object | null): any;
|
||||||
|
export = setPrototypeOf;
|
||||||
@ -0,0 +1,973 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { EventEmitter } = require('events');
|
||||||
|
const fs = require('fs');
|
||||||
|
const sysPath = require('path');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const readdirp = require('readdirp');
|
||||||
|
const anymatch = require('anymatch').default;
|
||||||
|
const globParent = require('glob-parent');
|
||||||
|
const isGlob = require('is-glob');
|
||||||
|
const braces = require('braces');
|
||||||
|
const normalizePath = require('normalize-path');
|
||||||
|
|
||||||
|
const NodeFsHandler = require('./lib/nodefs-handler');
|
||||||
|
const FsEventsHandler = require('./lib/fsevents-handler');
|
||||||
|
const {
|
||||||
|
EV_ALL,
|
||||||
|
EV_READY,
|
||||||
|
EV_ADD,
|
||||||
|
EV_CHANGE,
|
||||||
|
EV_UNLINK,
|
||||||
|
EV_ADD_DIR,
|
||||||
|
EV_UNLINK_DIR,
|
||||||
|
EV_RAW,
|
||||||
|
EV_ERROR,
|
||||||
|
|
||||||
|
STR_CLOSE,
|
||||||
|
STR_END,
|
||||||
|
|
||||||
|
BACK_SLASH_RE,
|
||||||
|
DOUBLE_SLASH_RE,
|
||||||
|
SLASH_OR_BACK_SLASH_RE,
|
||||||
|
DOT_RE,
|
||||||
|
REPLACER_RE,
|
||||||
|
|
||||||
|
SLASH,
|
||||||
|
SLASH_SLASH,
|
||||||
|
BRACE_START,
|
||||||
|
BANG,
|
||||||
|
ONE_DOT,
|
||||||
|
TWO_DOTS,
|
||||||
|
GLOBSTAR,
|
||||||
|
SLASH_GLOBSTAR,
|
||||||
|
ANYMATCH_OPTS,
|
||||||
|
STRING_TYPE,
|
||||||
|
FUNCTION_TYPE,
|
||||||
|
EMPTY_STR,
|
||||||
|
EMPTY_FN,
|
||||||
|
|
||||||
|
isWindows,
|
||||||
|
isMacos,
|
||||||
|
isIBMi
|
||||||
|
} = require('./lib/constants');
|
||||||
|
|
||||||
|
const stat = promisify(fs.stat);
|
||||||
|
const readdir = promisify(fs.readdir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {String} Path
|
||||||
|
* @typedef {'all'|'add'|'addDir'|'change'|'unlink'|'unlinkDir'|'raw'|'error'|'ready'} EventName
|
||||||
|
* @typedef {'readdir'|'watch'|'add'|'remove'|'change'} ThrottleType
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @typedef {Object} WatchHelpers
|
||||||
|
* @property {Boolean} followSymlinks
|
||||||
|
* @property {'stat'|'lstat'} statMethod
|
||||||
|
* @property {Path} path
|
||||||
|
* @property {Path} watchPath
|
||||||
|
* @property {Function} entryPath
|
||||||
|
* @property {Boolean} hasGlob
|
||||||
|
* @property {Object} globFilter
|
||||||
|
* @property {Function} filterPath
|
||||||
|
* @property {Function} filterDir
|
||||||
|
*/
|
||||||
|
|
||||||
|
const arrify = (value = []) => Array.isArray(value) ? value : [value];
|
||||||
|
const flatten = (list, result = []) => {
|
||||||
|
list.forEach(item => {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
flatten(item, result);
|
||||||
|
} else {
|
||||||
|
result.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const unifyPaths = (paths_) => {
|
||||||
|
/**
|
||||||
|
* @type {Array<String>}
|
||||||
|
*/
|
||||||
|
const paths = flatten(arrify(paths_));
|
||||||
|
if (!paths.every(p => typeof p === STRING_TYPE)) {
|
||||||
|
throw new TypeError(`Non-string provided as watch path: ${paths}`);
|
||||||
|
}
|
||||||
|
return paths.map(normalizePathToUnix);
|
||||||
|
};
|
||||||
|
|
||||||
|
// If SLASH_SLASH occurs at the beginning of path, it is not replaced
|
||||||
|
// because "//StoragePC/DrivePool/Movies" is a valid network path
|
||||||
|
const toUnix = (string) => {
|
||||||
|
let str = string.replace(BACK_SLASH_RE, SLASH);
|
||||||
|
let prepend = false;
|
||||||
|
if (str.startsWith(SLASH_SLASH)) {
|
||||||
|
prepend = true;
|
||||||
|
}
|
||||||
|
while (str.match(DOUBLE_SLASH_RE)) {
|
||||||
|
str = str.replace(DOUBLE_SLASH_RE, SLASH);
|
||||||
|
}
|
||||||
|
if (prepend) {
|
||||||
|
str = SLASH + str;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Our version of upath.normalize
|
||||||
|
// TODO: this is not equal to path-normalize module - investigate why
|
||||||
|
const normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));
|
||||||
|
|
||||||
|
const normalizeIgnored = (cwd = EMPTY_STR) => (path) => {
|
||||||
|
if (typeof path !== STRING_TYPE) return path;
|
||||||
|
return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAbsolutePath = (path, cwd) => {
|
||||||
|
if (sysPath.isAbsolute(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
if (path.startsWith(BANG)) {
|
||||||
|
return BANG + sysPath.join(cwd, path.slice(1));
|
||||||
|
}
|
||||||
|
return sysPath.join(cwd, path);
|
||||||
|
};
|
||||||
|
|
||||||
|
const undef = (opts, key) => opts[key] === undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory entry.
|
||||||
|
* @property {Path} path
|
||||||
|
* @property {Set<Path>} items
|
||||||
|
*/
|
||||||
|
class DirEntry {
|
||||||
|
/**
|
||||||
|
* @param {Path} dir
|
||||||
|
* @param {Function} removeWatcher
|
||||||
|
*/
|
||||||
|
constructor(dir, removeWatcher) {
|
||||||
|
this.path = dir;
|
||||||
|
this._removeWatcher = removeWatcher;
|
||||||
|
/** @type {Set<Path>} */
|
||||||
|
this.items = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
add(item) {
|
||||||
|
const {items} = this;
|
||||||
|
if (!items) return;
|
||||||
|
if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(item) {
|
||||||
|
const {items} = this;
|
||||||
|
if (!items) return;
|
||||||
|
items.delete(item);
|
||||||
|
if (items.size > 0) return;
|
||||||
|
|
||||||
|
const dir = this.path;
|
||||||
|
try {
|
||||||
|
await readdir(dir);
|
||||||
|
} catch (err) {
|
||||||
|
if (this._removeWatcher) {
|
||||||
|
this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
has(item) {
|
||||||
|
const {items} = this;
|
||||||
|
if (!items) return;
|
||||||
|
return items.has(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Array<String>}
|
||||||
|
*/
|
||||||
|
getChildren() {
|
||||||
|
const {items} = this;
|
||||||
|
if (!items) return;
|
||||||
|
return [...items.values()];
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.items.clear();
|
||||||
|
delete this.path;
|
||||||
|
delete this._removeWatcher;
|
||||||
|
delete this.items;
|
||||||
|
Object.freeze(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const STAT_METHOD_F = 'stat';
|
||||||
|
const STAT_METHOD_L = 'lstat';
|
||||||
|
class WatchHelper {
|
||||||
|
constructor(path, watchPath, follow, fsw) {
|
||||||
|
this.fsw = fsw;
|
||||||
|
this.path = path = path.replace(REPLACER_RE, EMPTY_STR);
|
||||||
|
this.watchPath = watchPath;
|
||||||
|
this.fullWatchPath = sysPath.resolve(watchPath);
|
||||||
|
this.hasGlob = watchPath !== path;
|
||||||
|
/** @type {object|boolean} */
|
||||||
|
if (path === EMPTY_STR) this.hasGlob = false;
|
||||||
|
this.globSymlink = this.hasGlob && follow ? undefined : false;
|
||||||
|
this.globFilter = this.hasGlob ? anymatch(path, undefined, ANYMATCH_OPTS) : false;
|
||||||
|
this.dirParts = this.getDirParts(path);
|
||||||
|
this.dirParts.forEach((parts) => {
|
||||||
|
if (parts.length > 1) parts.pop();
|
||||||
|
});
|
||||||
|
this.followSymlinks = follow;
|
||||||
|
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkGlobSymlink(entry) {
|
||||||
|
// only need to resolve once
|
||||||
|
// first entry should always have entry.parentDir === EMPTY_STR
|
||||||
|
if (this.globSymlink === undefined) {
|
||||||
|
this.globSymlink = entry.fullParentDir === this.fullWatchPath ?
|
||||||
|
false : {realPath: entry.fullParentDir, linkPath: this.fullWatchPath};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.globSymlink) {
|
||||||
|
return entry.fullPath.replace(this.globSymlink.realPath, this.globSymlink.linkPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPath(entry) {
|
||||||
|
return sysPath.join(this.watchPath,
|
||||||
|
sysPath.relative(this.watchPath, this.checkGlobSymlink(entry))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
filterPath(entry) {
|
||||||
|
const {stats} = entry;
|
||||||
|
if (stats && stats.isSymbolicLink()) return this.filterDir(entry);
|
||||||
|
const resolvedPath = this.entryPath(entry);
|
||||||
|
const matchesGlob = this.hasGlob && typeof this.globFilter === FUNCTION_TYPE ?
|
||||||
|
this.globFilter(resolvedPath) : true;
|
||||||
|
return matchesGlob &&
|
||||||
|
this.fsw._isntIgnored(resolvedPath, stats) &&
|
||||||
|
this.fsw._hasReadPermissions(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDirParts(path) {
|
||||||
|
if (!this.hasGlob) return [];
|
||||||
|
const parts = [];
|
||||||
|
const expandedPath = path.includes(BRACE_START) ? braces.expand(path) : [path];
|
||||||
|
expandedPath.forEach((path) => {
|
||||||
|
parts.push(sysPath.relative(this.watchPath, path).split(SLASH_OR_BACK_SLASH_RE));
|
||||||
|
});
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterDir(entry) {
|
||||||
|
if (this.hasGlob) {
|
||||||
|
const entryParts = this.getDirParts(this.checkGlobSymlink(entry));
|
||||||
|
let globstar = false;
|
||||||
|
this.unmatchedGlob = !this.dirParts.some((parts) => {
|
||||||
|
return parts.every((part, i) => {
|
||||||
|
if (part === GLOBSTAR) globstar = true;
|
||||||
|
return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i], ANYMATCH_OPTS);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return !this.unmatchedGlob && this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watches files & directories for changes. Emitted events:
|
||||||
|
* `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
|
||||||
|
*
|
||||||
|
* new FSWatcher()
|
||||||
|
* .add(directories)
|
||||||
|
* .on('add', path => log('File', path, 'was added'))
|
||||||
|
*/
|
||||||
|
class FSWatcher extends EventEmitter {
|
||||||
|
// Not indenting methods for history sake; for now.
|
||||||
|
constructor(_opts) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const opts = {};
|
||||||
|
if (_opts) Object.assign(opts, _opts); // for frozen objects
|
||||||
|
|
||||||
|
/** @type {Map<String, DirEntry>} */
|
||||||
|
this._watched = new Map();
|
||||||
|
/** @type {Map<String, Array>} */
|
||||||
|
this._closers = new Map();
|
||||||
|
/** @type {Set<String>} */
|
||||||
|
this._ignoredPaths = new Set();
|
||||||
|
|
||||||
|
/** @type {Map<ThrottleType, Map>} */
|
||||||
|
this._throttled = new Map();
|
||||||
|
|
||||||
|
/** @type {Map<Path, String|Boolean>} */
|
||||||
|
this._symlinkPaths = new Map();
|
||||||
|
|
||||||
|
this._streams = new Set();
|
||||||
|
this.closed = false;
|
||||||
|
|
||||||
|
// Set up default options.
|
||||||
|
if (undef(opts, 'persistent')) opts.persistent = true;
|
||||||
|
if (undef(opts, 'ignoreInitial')) opts.ignoreInitial = false;
|
||||||
|
if (undef(opts, 'ignorePermissionErrors')) opts.ignorePermissionErrors = false;
|
||||||
|
if (undef(opts, 'interval')) opts.interval = 100;
|
||||||
|
if (undef(opts, 'binaryInterval')) opts.binaryInterval = 300;
|
||||||
|
if (undef(opts, 'disableGlobbing')) opts.disableGlobbing = false;
|
||||||
|
opts.enableBinaryInterval = opts.binaryInterval !== opts.interval;
|
||||||
|
|
||||||
|
// Enable fsevents on OS X when polling isn't explicitly enabled.
|
||||||
|
if (undef(opts, 'useFsEvents')) opts.useFsEvents = !opts.usePolling;
|
||||||
|
|
||||||
|
// If we can't use fsevents, ensure the options reflect it's disabled.
|
||||||
|
const canUseFsEvents = FsEventsHandler.canUse();
|
||||||
|
if (!canUseFsEvents) opts.useFsEvents = false;
|
||||||
|
|
||||||
|
// Use polling on Mac if not using fsevents.
|
||||||
|
// Other platforms use non-polling fs_watch.
|
||||||
|
if (undef(opts, 'usePolling') && !opts.useFsEvents) {
|
||||||
|
opts.usePolling = isMacos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always default to polling on IBM i because fs.watch() is not available on IBM i.
|
||||||
|
if(isIBMi) {
|
||||||
|
opts.usePolling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global override (useful for end-developers that need to force polling for all
|
||||||
|
// instances of chokidar, regardless of usage/dependency depth)
|
||||||
|
const envPoll = process.env.CHOKIDAR_USEPOLLING;
|
||||||
|
if (envPoll !== undefined) {
|
||||||
|
const envLower = envPoll.toLowerCase();
|
||||||
|
|
||||||
|
if (envLower === 'false' || envLower === '0') {
|
||||||
|
opts.usePolling = false;
|
||||||
|
} else if (envLower === 'true' || envLower === '1') {
|
||||||
|
opts.usePolling = true;
|
||||||
|
} else {
|
||||||
|
opts.usePolling = !!envLower;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const envInterval = process.env.CHOKIDAR_INTERVAL;
|
||||||
|
if (envInterval) {
|
||||||
|
opts.interval = Number.parseInt(envInterval, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Editor atomic write normalization enabled by default with fs.watch
|
||||||
|
if (undef(opts, 'atomic')) opts.atomic = !opts.usePolling && !opts.useFsEvents;
|
||||||
|
if (opts.atomic) this._pendingUnlinks = new Map();
|
||||||
|
|
||||||
|
if (undef(opts, 'followSymlinks')) opts.followSymlinks = true;
|
||||||
|
|
||||||
|
if (undef(opts, 'awaitWriteFinish')) opts.awaitWriteFinish = false;
|
||||||
|
if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {};
|
||||||
|
const awf = opts.awaitWriteFinish;
|
||||||
|
if (awf) {
|
||||||
|
if (!awf.stabilityThreshold) awf.stabilityThreshold = 2000;
|
||||||
|
if (!awf.pollInterval) awf.pollInterval = 100;
|
||||||
|
this._pendingWrites = new Map();
|
||||||
|
}
|
||||||
|
if (opts.ignored) opts.ignored = arrify(opts.ignored);
|
||||||
|
|
||||||
|
let readyCalls = 0;
|
||||||
|
this._emitReady = () => {
|
||||||
|
readyCalls++;
|
||||||
|
if (readyCalls >= this._readyCount) {
|
||||||
|
this._emitReady = EMPTY_FN;
|
||||||
|
this._readyEmitted = true;
|
||||||
|
// use process.nextTick to allow time for listener to be bound
|
||||||
|
process.nextTick(() => this.emit(EV_READY));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this._emitRaw = (...args) => this.emit(EV_RAW, ...args);
|
||||||
|
this._readyEmitted = false;
|
||||||
|
this.options = opts;
|
||||||
|
|
||||||
|
// Initialize with proper watcher.
|
||||||
|
if (opts.useFsEvents) {
|
||||||
|
this._fsEventsHandler = new FsEventsHandler(this);
|
||||||
|
} else {
|
||||||
|
this._nodeFsHandler = new NodeFsHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You’re frozen when your heart’s not open.
|
||||||
|
Object.freeze(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds paths to be watched on an existing FSWatcher instance
|
||||||
|
* @param {Path|Array<Path>} paths_
|
||||||
|
* @param {String=} _origAdd private; for handling non-existent paths to be watched
|
||||||
|
* @param {Boolean=} _internal private; indicates a non-user add
|
||||||
|
* @returns {FSWatcher} for chaining
|
||||||
|
*/
|
||||||
|
add(paths_, _origAdd, _internal) {
|
||||||
|
const {cwd, disableGlobbing} = this.options;
|
||||||
|
this.closed = false;
|
||||||
|
let paths = unifyPaths(paths_);
|
||||||
|
if (cwd) {
|
||||||
|
paths = paths.map((path) => {
|
||||||
|
const absPath = getAbsolutePath(path, cwd);
|
||||||
|
|
||||||
|
// Check `path` instead of `absPath` because the cwd portion can't be a glob
|
||||||
|
if (disableGlobbing || !isGlob(path)) {
|
||||||
|
return absPath;
|
||||||
|
}
|
||||||
|
return normalizePath(absPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// set aside negated glob strings
|
||||||
|
paths = paths.filter((path) => {
|
||||||
|
if (path.startsWith(BANG)) {
|
||||||
|
this._ignoredPaths.add(path.slice(1));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a path is being added that was previously ignored, stop ignoring it
|
||||||
|
this._ignoredPaths.delete(path);
|
||||||
|
this._ignoredPaths.delete(path + SLASH_GLOBSTAR);
|
||||||
|
|
||||||
|
// reset the cached userIgnored anymatch fn
|
||||||
|
// to make ignoredPaths changes effective
|
||||||
|
this._userIgnored = undefined;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.options.useFsEvents && this._fsEventsHandler) {
|
||||||
|
if (!this._readyCount) this._readyCount = paths.length;
|
||||||
|
if (this.options.persistent) this._readyCount += paths.length;
|
||||||
|
paths.forEach((path) => this._fsEventsHandler._addToFsEvents(path));
|
||||||
|
} else {
|
||||||
|
if (!this._readyCount) this._readyCount = 0;
|
||||||
|
this._readyCount += paths.length;
|
||||||
|
Promise.all(
|
||||||
|
paths.map(async path => {
|
||||||
|
const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, 0, 0, _origAdd);
|
||||||
|
if (res) this._emitReady();
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
).then(results => {
|
||||||
|
if (this.closed) return;
|
||||||
|
results.filter(item => item).forEach(item => {
|
||||||
|
this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close watchers or start ignoring events from specified paths.
|
||||||
|
* @param {Path|Array<Path>} paths_ - string or array of strings, file/directory paths and/or globs
|
||||||
|
* @returns {FSWatcher} for chaining
|
||||||
|
*/
|
||||||
|
unwatch(paths_) {
|
||||||
|
if (this.closed) return this;
|
||||||
|
const paths = unifyPaths(paths_);
|
||||||
|
const {cwd} = this.options;
|
||||||
|
|
||||||
|
paths.forEach((path) => {
|
||||||
|
// convert to absolute path unless relative path already matches
|
||||||
|
if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {
|
||||||
|
if (cwd) path = sysPath.join(cwd, path);
|
||||||
|
path = sysPath.resolve(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._closePath(path);
|
||||||
|
|
||||||
|
this._ignoredPaths.add(path);
|
||||||
|
if (this._watched.has(path)) {
|
||||||
|
this._ignoredPaths.add(path + SLASH_GLOBSTAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the cached userIgnored anymatch fn
|
||||||
|
// to make ignoredPaths changes effective
|
||||||
|
this._userIgnored = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close watchers and remove all listeners from watched paths.
|
||||||
|
* @returns {Promise<void>}.
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
if (this.closed) return this._closePromise;
|
||||||
|
this.closed = true;
|
||||||
|
|
||||||
|
// Memory management.
|
||||||
|
this.removeAllListeners();
|
||||||
|
const closers = [];
|
||||||
|
this._closers.forEach(closerList => closerList.forEach(closer => {
|
||||||
|
const promise = closer();
|
||||||
|
if (promise instanceof Promise) closers.push(promise);
|
||||||
|
}));
|
||||||
|
this._streams.forEach(stream => stream.destroy());
|
||||||
|
this._userIgnored = undefined;
|
||||||
|
this._readyCount = 0;
|
||||||
|
this._readyEmitted = false;
|
||||||
|
this._watched.forEach(dirent => dirent.dispose());
|
||||||
|
['closers', 'watched', 'streams', 'symlinkPaths', 'throttled'].forEach(key => {
|
||||||
|
this[`_${key}`].clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._closePromise = closers.length ? Promise.all(closers).then(() => undefined) : Promise.resolve();
|
||||||
|
return this._closePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose list of watched paths
|
||||||
|
* @returns {Object} for chaining
|
||||||
|
*/
|
||||||
|
getWatched() {
|
||||||
|
const watchList = {};
|
||||||
|
this._watched.forEach((entry, dir) => {
|
||||||
|
const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
|
||||||
|
watchList[key || ONE_DOT] = entry.getChildren().sort();
|
||||||
|
});
|
||||||
|
return watchList;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitWithAll(event, args) {
|
||||||
|
this.emit(...args);
|
||||||
|
if (event !== EV_ERROR) this.emit(EV_ALL, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common helpers
|
||||||
|
// --------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize and emit events.
|
||||||
|
* Calling _emit DOES NOT MEAN emit() would be called!
|
||||||
|
* @param {EventName} event Type of event
|
||||||
|
* @param {Path} path File or directory path
|
||||||
|
* @param {*=} val1 arguments to be passed with event
|
||||||
|
* @param {*=} val2
|
||||||
|
* @param {*=} val3
|
||||||
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
||||||
|
*/
|
||||||
|
async _emit(event, path, val1, val2, val3) {
|
||||||
|
if (this.closed) return;
|
||||||
|
|
||||||
|
const opts = this.options;
|
||||||
|
if (isWindows) path = sysPath.normalize(path);
|
||||||
|
if (opts.cwd) path = sysPath.relative(opts.cwd, path);
|
||||||
|
/** @type Array<any> */
|
||||||
|
const args = [event, path];
|
||||||
|
if (val3 !== undefined) args.push(val1, val2, val3);
|
||||||
|
else if (val2 !== undefined) args.push(val1, val2);
|
||||||
|
else if (val1 !== undefined) args.push(val1);
|
||||||
|
|
||||||
|
const awf = opts.awaitWriteFinish;
|
||||||
|
let pw;
|
||||||
|
if (awf && (pw = this._pendingWrites.get(path))) {
|
||||||
|
pw.lastChange = new Date();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.atomic) {
|
||||||
|
if (event === EV_UNLINK) {
|
||||||
|
this._pendingUnlinks.set(path, args);
|
||||||
|
setTimeout(() => {
|
||||||
|
this._pendingUnlinks.forEach((entry, path) => {
|
||||||
|
this.emit(...entry);
|
||||||
|
this.emit(EV_ALL, ...entry);
|
||||||
|
this._pendingUnlinks.delete(path);
|
||||||
|
});
|
||||||
|
}, typeof opts.atomic === 'number' ? opts.atomic : 100);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (event === EV_ADD && this._pendingUnlinks.has(path)) {
|
||||||
|
event = args[0] = EV_CHANGE;
|
||||||
|
this._pendingUnlinks.delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) {
|
||||||
|
const awfEmit = (err, stats) => {
|
||||||
|
if (err) {
|
||||||
|
event = args[0] = EV_ERROR;
|
||||||
|
args[1] = err;
|
||||||
|
this.emitWithAll(event, args);
|
||||||
|
} else if (stats) {
|
||||||
|
// if stats doesn't exist the file must have been deleted
|
||||||
|
if (args.length > 2) {
|
||||||
|
args[2] = stats;
|
||||||
|
} else {
|
||||||
|
args.push(stats);
|
||||||
|
}
|
||||||
|
this.emitWithAll(event, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event === EV_CHANGE) {
|
||||||
|
const isThrottled = !this._throttle(EV_CHANGE, path, 50);
|
||||||
|
if (isThrottled) return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.alwaysStat && val1 === undefined &&
|
||||||
|
(event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)
|
||||||
|
) {
|
||||||
|
const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;
|
||||||
|
let stats;
|
||||||
|
try {
|
||||||
|
stats = await stat(fullPath);
|
||||||
|
} catch (err) {}
|
||||||
|
// Suppress event when fs_stat fails, to avoid sending undefined 'stat'
|
||||||
|
if (!stats || this.closed) return;
|
||||||
|
args.push(stats);
|
||||||
|
}
|
||||||
|
this.emitWithAll(event, args);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common handler for errors
|
||||||
|
* @param {Error} error
|
||||||
|
* @returns {Error|Boolean} The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
||||||
|
*/
|
||||||
|
_handleError(error) {
|
||||||
|
const code = error && error.code;
|
||||||
|
if (error && code !== 'ENOENT' && code !== 'ENOTDIR' &&
|
||||||
|
(!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))
|
||||||
|
) {
|
||||||
|
this.emit(EV_ERROR, error);
|
||||||
|
}
|
||||||
|
return error || this.closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper utility for throttling
|
||||||
|
* @param {ThrottleType} actionType type being throttled
|
||||||
|
* @param {Path} path being acted upon
|
||||||
|
* @param {Number} timeout duration of time to suppress duplicate actions
|
||||||
|
* @returns {Object|false} tracking object or false if action should be suppressed
|
||||||
|
*/
|
||||||
|
_throttle(actionType, path, timeout) {
|
||||||
|
if (!this._throttled.has(actionType)) {
|
||||||
|
this._throttled.set(actionType, new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Map<Path, Object>} */
|
||||||
|
const action = this._throttled.get(actionType);
|
||||||
|
/** @type {Object} */
|
||||||
|
const actionPath = action.get(path);
|
||||||
|
|
||||||
|
if (actionPath) {
|
||||||
|
actionPath.count++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeoutObject;
|
||||||
|
const clear = () => {
|
||||||
|
const item = action.get(path);
|
||||||
|
const count = item ? item.count : 0;
|
||||||
|
action.delete(path);
|
||||||
|
clearTimeout(timeoutObject);
|
||||||
|
if (item) clearTimeout(item.timeoutObject);
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
timeoutObject = setTimeout(clear, timeout);
|
||||||
|
const thr = {timeoutObject, clear, count: 0};
|
||||||
|
action.set(path, thr);
|
||||||
|
return thr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_incrReadyCount() {
|
||||||
|
return this._readyCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Awaits write operation to finish.
|
||||||
|
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
|
||||||
|
* @param {Path} path being acted upon
|
||||||
|
* @param {Number} threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
|
||||||
|
* @param {EventName} event
|
||||||
|
* @param {Function} awfEmit Callback to be called when ready for event to be emitted.
|
||||||
|
*/
|
||||||
|
_awaitWriteFinish(path, threshold, event, awfEmit) {
|
||||||
|
let timeoutHandler;
|
||||||
|
|
||||||
|
let fullPath = path;
|
||||||
|
if (this.options.cwd && !sysPath.isAbsolute(path)) {
|
||||||
|
fullPath = sysPath.join(this.options.cwd, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const awaitWriteFinish = (prevStat) => {
|
||||||
|
fs.stat(fullPath, (err, curStat) => {
|
||||||
|
if (err || !this._pendingWrites.has(path)) {
|
||||||
|
if (err && err.code !== 'ENOENT') awfEmit(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = Number(new Date());
|
||||||
|
|
||||||
|
if (prevStat && curStat.size !== prevStat.size) {
|
||||||
|
this._pendingWrites.get(path).lastChange = now;
|
||||||
|
}
|
||||||
|
const pw = this._pendingWrites.get(path);
|
||||||
|
const df = now - pw.lastChange;
|
||||||
|
|
||||||
|
if (df >= threshold) {
|
||||||
|
this._pendingWrites.delete(path);
|
||||||
|
awfEmit(undefined, curStat);
|
||||||
|
} else {
|
||||||
|
timeoutHandler = setTimeout(
|
||||||
|
awaitWriteFinish,
|
||||||
|
this.options.awaitWriteFinish.pollInterval,
|
||||||
|
curStat
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this._pendingWrites.has(path)) {
|
||||||
|
this._pendingWrites.set(path, {
|
||||||
|
lastChange: now,
|
||||||
|
cancelWait: () => {
|
||||||
|
this._pendingWrites.delete(path);
|
||||||
|
clearTimeout(timeoutHandler);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
timeoutHandler = setTimeout(
|
||||||
|
awaitWriteFinish,
|
||||||
|
this.options.awaitWriteFinish.pollInterval
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getGlobIgnored() {
|
||||||
|
return [...this._ignoredPaths.values()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether user has asked to ignore this path.
|
||||||
|
* @param {Path} path filepath or dir
|
||||||
|
* @param {fs.Stats=} stats result of fs.stat
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
_isIgnored(path, stats) {
|
||||||
|
if (this.options.atomic && DOT_RE.test(path)) return true;
|
||||||
|
if (!this._userIgnored) {
|
||||||
|
const {cwd} = this.options;
|
||||||
|
const ign = this.options.ignored;
|
||||||
|
|
||||||
|
const ignored = ign && ign.map(normalizeIgnored(cwd));
|
||||||
|
const paths = arrify(ignored)
|
||||||
|
.filter((path) => typeof path === STRING_TYPE && !isGlob(path))
|
||||||
|
.map((path) => path + SLASH_GLOBSTAR);
|
||||||
|
const list = this._getGlobIgnored().map(normalizeIgnored(cwd)).concat(ignored, paths);
|
||||||
|
this._userIgnored = anymatch(list, undefined, ANYMATCH_OPTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._userIgnored([path, stats]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isntIgnored(path, stat) {
|
||||||
|
return !this._isIgnored(path, stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a set of common helpers and properties relating to symlink and glob handling.
|
||||||
|
* @param {Path} path file, directory, or glob pattern being watched
|
||||||
|
* @param {Number=} depth at any depth > 0, this isn't a glob
|
||||||
|
* @returns {WatchHelper} object containing helpers for this path
|
||||||
|
*/
|
||||||
|
_getWatchHelpers(path, depth) {
|
||||||
|
const watchPath = depth || this.options.disableGlobbing || !isGlob(path) ? path : globParent(path);
|
||||||
|
const follow = this.options.followSymlinks;
|
||||||
|
|
||||||
|
return new WatchHelper(path, watchPath, follow, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directory helpers
|
||||||
|
// -----------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides directory tracking objects
|
||||||
|
* @param {String} directory path of the directory
|
||||||
|
* @returns {DirEntry} the directory's tracking object
|
||||||
|
*/
|
||||||
|
_getWatchedDir(directory) {
|
||||||
|
if (!this._boundRemove) this._boundRemove = this._remove.bind(this);
|
||||||
|
const dir = sysPath.resolve(directory);
|
||||||
|
if (!this._watched.has(dir)) this._watched.set(dir, new DirEntry(dir, this._boundRemove));
|
||||||
|
return this._watched.get(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// File helpers
|
||||||
|
// ------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for read permissions.
|
||||||
|
* Based on this answer on SO: https://stackoverflow.com/a/11781404/1358405
|
||||||
|
* @param {fs.Stats} stats - object, result of fs_stat
|
||||||
|
* @returns {Boolean} indicates whether the file can be read
|
||||||
|
*/
|
||||||
|
_hasReadPermissions(stats) {
|
||||||
|
if (this.options.ignorePermissionErrors) return true;
|
||||||
|
|
||||||
|
// stats.mode may be bigint
|
||||||
|
const md = stats && Number.parseInt(stats.mode, 10);
|
||||||
|
const st = md & 0o777;
|
||||||
|
const it = Number.parseInt(st.toString(8)[0], 10);
|
||||||
|
return Boolean(4 & it);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles emitting unlink events for
|
||||||
|
* files and directories, and via recursion, for
|
||||||
|
* files and directories within directories that are unlinked
|
||||||
|
* @param {String} directory within which the following item is located
|
||||||
|
* @param {String} item base path of item/directory
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_remove(directory, item, isDirectory) {
|
||||||
|
// if what is being deleted is a directory, get that directory's paths
|
||||||
|
// for recursive deleting and cleaning of watched object
|
||||||
|
// if it is not a directory, nestedDirectoryChildren will be empty array
|
||||||
|
const path = sysPath.join(directory, item);
|
||||||
|
const fullPath = sysPath.resolve(path);
|
||||||
|
isDirectory = isDirectory != null
|
||||||
|
? isDirectory
|
||||||
|
: this._watched.has(path) || this._watched.has(fullPath);
|
||||||
|
|
||||||
|
// prevent duplicate handling in case of arriving here nearly simultaneously
|
||||||
|
// via multiple paths (such as _handleFile and _handleDir)
|
||||||
|
if (!this._throttle('remove', path, 100)) return;
|
||||||
|
|
||||||
|
// if the only watched file is removed, watch for its return
|
||||||
|
if (!isDirectory && !this.options.useFsEvents && this._watched.size === 1) {
|
||||||
|
this.add(directory, item, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will create a new entry in the watched object in either case
|
||||||
|
// so we got to do the directory check beforehand
|
||||||
|
const wp = this._getWatchedDir(path);
|
||||||
|
const nestedDirectoryChildren = wp.getChildren();
|
||||||
|
|
||||||
|
// Recursively remove children directories / files.
|
||||||
|
nestedDirectoryChildren.forEach(nested => this._remove(path, nested));
|
||||||
|
|
||||||
|
// Check if item was on the watched list and remove it
|
||||||
|
const parent = this._getWatchedDir(directory);
|
||||||
|
const wasTracked = parent.has(item);
|
||||||
|
parent.remove(item);
|
||||||
|
|
||||||
|
// Fixes issue #1042 -> Relative paths were detected and added as symlinks
|
||||||
|
// (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),
|
||||||
|
// but never removed from the map in case the path was deleted.
|
||||||
|
// This leads to an incorrect state if the path was recreated:
|
||||||
|
// https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553
|
||||||
|
if (this._symlinkPaths.has(fullPath)) {
|
||||||
|
this._symlinkPaths.delete(fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we wait for this file to be fully written, cancel the wait.
|
||||||
|
let relPath = path;
|
||||||
|
if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path);
|
||||||
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
||||||
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
||||||
|
if (event === EV_ADD) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Entry will either be a directory that just got removed
|
||||||
|
// or a bogus entry to a file, in either case we have to remove it
|
||||||
|
this._watched.delete(path);
|
||||||
|
this._watched.delete(fullPath);
|
||||||
|
const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK;
|
||||||
|
if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path);
|
||||||
|
|
||||||
|
// Avoid conflicts if we later create another file with the same name
|
||||||
|
if (!this.options.useFsEvents) {
|
||||||
|
this._closePath(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes all watchers for a path
|
||||||
|
* @param {Path} path
|
||||||
|
*/
|
||||||
|
_closePath(path) {
|
||||||
|
this._closeFile(path)
|
||||||
|
const dir = sysPath.dirname(path);
|
||||||
|
this._getWatchedDir(dir).remove(sysPath.basename(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes only file-specific watchers
|
||||||
|
* @param {Path} path
|
||||||
|
*/
|
||||||
|
_closeFile(path) {
|
||||||
|
const closers = this._closers.get(path);
|
||||||
|
if (!closers) return;
|
||||||
|
closers.forEach(closer => closer());
|
||||||
|
this._closers.delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Path} path
|
||||||
|
* @param {Function} closer
|
||||||
|
*/
|
||||||
|
_addPathCloser(path, closer) {
|
||||||
|
if (!closer) return;
|
||||||
|
let list = this._closers.get(path);
|
||||||
|
if (!list) {
|
||||||
|
list = [];
|
||||||
|
this._closers.set(path, list);
|
||||||
|
}
|
||||||
|
list.push(closer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_readdirp(root, opts) {
|
||||||
|
if (this.closed) return;
|
||||||
|
const options = {type: EV_ALL, alwaysStat: true, lstat: true, ...opts};
|
||||||
|
let stream = readdirp(root, options);
|
||||||
|
this._streams.add(stream);
|
||||||
|
stream.once(STR_CLOSE, () => {
|
||||||
|
stream = undefined;
|
||||||
|
});
|
||||||
|
stream.once(STR_END, () => {
|
||||||
|
if (stream) {
|
||||||
|
this._streams.delete(stream);
|
||||||
|
stream = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export FSWatcher class
|
||||||
|
exports.FSWatcher = FSWatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates watcher with paths to be tracked.
|
||||||
|
* @param {String|Array<String>} paths file/directory paths and/or globs
|
||||||
|
* @param {Object=} options chokidar opts
|
||||||
|
* @returns an instance of FSWatcher for chaining.
|
||||||
|
*/
|
||||||
|
const watch = (paths, options) => {
|
||||||
|
const watcher = new FSWatcher(options);
|
||||||
|
watcher.add(paths);
|
||||||
|
return watcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.watch = watch;
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
module.exports = (ast, options = {}) => {
|
||||||
|
const stringify = (node, parent = {}) => {
|
||||||
|
const invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
|
||||||
|
const invalidNode = node.invalid === true && options.escapeInvalid === true;
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
if (node.value) {
|
||||||
|
if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) {
|
||||||
|
return '\\' + node.value;
|
||||||
|
}
|
||||||
|
return node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.value) {
|
||||||
|
return node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodes) {
|
||||||
|
for (const child of node.nodes) {
|
||||||
|
output += stringify(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
return stringify(ast);
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1,261 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var inspect = require('../');
|
||||||
|
var test = require('tape');
|
||||||
|
var mockProperty = require('mock-property');
|
||||||
|
var hasSymbols = require('has-symbols/shams')();
|
||||||
|
var hasToStringTag = require('has-tostringtag/shams')();
|
||||||
|
var forEach = require('for-each');
|
||||||
|
var semver = require('semver');
|
||||||
|
|
||||||
|
test('values', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
var obj = [{}, [], { 'a-b': 5 }];
|
||||||
|
t.equal(inspect(obj), '[ {}, [], { \'a-b\': 5 } ]');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('arrays with properties', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
var arr = [3];
|
||||||
|
arr.foo = 'bar';
|
||||||
|
var obj = [1, 2, arr];
|
||||||
|
obj.baz = 'quux';
|
||||||
|
obj.index = -1;
|
||||||
|
t.equal(inspect(obj), '[ 1, 2, [ 3, foo: \'bar\' ], baz: \'quux\', index: -1 ]');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('has', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
t.teardown(mockProperty(Object.prototype, 'hasOwnProperty', { 'delete': true }));
|
||||||
|
|
||||||
|
t.equal(inspect({ a: 1, b: 2 }), '{ a: 1, b: 2 }');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('indexOf seen', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
var xs = [1, 2, 3, {}];
|
||||||
|
xs.push(xs);
|
||||||
|
|
||||||
|
var seen = [];
|
||||||
|
seen.indexOf = undefined;
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
inspect(xs, {}, 0, seen),
|
||||||
|
'[ 1, 2, 3, {}, [Circular] ]'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('seen seen', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
var xs = [1, 2, 3];
|
||||||
|
|
||||||
|
var seen = [xs];
|
||||||
|
seen.indexOf = undefined;
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
inspect(xs, {}, 0, seen),
|
||||||
|
'[Circular]'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('seen seen seen', function (t) {
|
||||||
|
t.plan(1);
|
||||||
|
var xs = [1, 2, 3];
|
||||||
|
|
||||||
|
var seen = [5, xs];
|
||||||
|
seen.indexOf = undefined;
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
inspect(xs, {}, 0, seen),
|
||||||
|
'[Circular]'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('symbols', { skip: !hasSymbols }, function (t) {
|
||||||
|
var sym = Symbol('foo');
|
||||||
|
t.equal(inspect(sym), 'Symbol(foo)', 'Symbol("foo") should be "Symbol(foo)"');
|
||||||
|
if (typeof sym === 'symbol') {
|
||||||
|
// Symbol shams are incapable of differentiating boxed from unboxed symbols
|
||||||
|
t.equal(inspect(Object(sym)), 'Object(Symbol(foo))', 'Object(Symbol("foo")) should be "Object(Symbol(foo))"');
|
||||||
|
}
|
||||||
|
|
||||||
|
t.test('toStringTag', { skip: !hasToStringTag }, function (st) {
|
||||||
|
st.plan(1);
|
||||||
|
|
||||||
|
var faker = {};
|
||||||
|
faker[Symbol.toStringTag] = 'Symbol';
|
||||||
|
st.equal(
|
||||||
|
inspect(faker),
|
||||||
|
'{ [Symbol(Symbol.toStringTag)]: \'Symbol\' }',
|
||||||
|
'object lying about being a Symbol inspects as an object'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Map', { skip: typeof Map !== 'function' }, function (t) {
|
||||||
|
var map = new Map();
|
||||||
|
map.set({ a: 1 }, ['b']);
|
||||||
|
map.set(3, NaN);
|
||||||
|
var expectedString = 'Map (2) {' + inspect({ a: 1 }) + ' => ' + inspect(['b']) + ', 3 => NaN}';
|
||||||
|
t.equal(inspect(map), expectedString, 'new Map([[{ a: 1 }, ["b"]], [3, NaN]]) should show size and contents');
|
||||||
|
t.equal(inspect(new Map()), 'Map (0) {}', 'empty Map should show as empty');
|
||||||
|
|
||||||
|
var nestedMap = new Map();
|
||||||
|
nestedMap.set(nestedMap, map);
|
||||||
|
t.equal(inspect(nestedMap), 'Map (1) {[Circular] => ' + expectedString + '}', 'Map containing a Map should work');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('WeakMap', { skip: typeof WeakMap !== 'function' }, function (t) {
|
||||||
|
var map = new WeakMap();
|
||||||
|
map.set({ a: 1 }, ['b']);
|
||||||
|
var expectedString = 'WeakMap { ? }';
|
||||||
|
t.equal(inspect(map), expectedString, 'new WeakMap([[{ a: 1 }, ["b"]]]) should not show size or contents');
|
||||||
|
t.equal(inspect(new WeakMap()), 'WeakMap { ? }', 'empty WeakMap should not show as empty');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Set', { skip: typeof Set !== 'function' }, function (t) {
|
||||||
|
var set = new Set();
|
||||||
|
set.add({ a: 1 });
|
||||||
|
set.add(['b']);
|
||||||
|
var expectedString = 'Set (2) {' + inspect({ a: 1 }) + ', ' + inspect(['b']) + '}';
|
||||||
|
t.equal(inspect(set), expectedString, 'new Set([{ a: 1 }, ["b"]]) should show size and contents');
|
||||||
|
t.equal(inspect(new Set()), 'Set (0) {}', 'empty Set should show as empty');
|
||||||
|
|
||||||
|
var nestedSet = new Set();
|
||||||
|
nestedSet.add(set);
|
||||||
|
nestedSet.add(nestedSet);
|
||||||
|
t.equal(inspect(nestedSet), 'Set (2) {' + expectedString + ', [Circular]}', 'Set containing a Set should work');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('WeakSet', { skip: typeof WeakSet !== 'function' }, function (t) {
|
||||||
|
var map = new WeakSet();
|
||||||
|
map.add({ a: 1 });
|
||||||
|
var expectedString = 'WeakSet { ? }';
|
||||||
|
t.equal(inspect(map), expectedString, 'new WeakSet([{ a: 1 }]) should not show size or contents');
|
||||||
|
t.equal(inspect(new WeakSet()), 'WeakSet { ? }', 'empty WeakSet should not show as empty');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('WeakRef', { skip: typeof WeakRef !== 'function' }, function (t) {
|
||||||
|
var ref = new WeakRef({ a: 1 });
|
||||||
|
var expectedString = 'WeakRef { ? }';
|
||||||
|
t.equal(inspect(ref), expectedString, 'new WeakRef({ a: 1 }) should not show contents');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('FinalizationRegistry', { skip: typeof FinalizationRegistry !== 'function' }, function (t) {
|
||||||
|
var registry = new FinalizationRegistry(function () {});
|
||||||
|
var expectedString = 'FinalizationRegistry [FinalizationRegistry] {}';
|
||||||
|
t.equal(inspect(registry), expectedString, 'new FinalizationRegistry(function () {}) should work normallys');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Strings', function (t) {
|
||||||
|
var str = 'abc';
|
||||||
|
|
||||||
|
t.equal(inspect(str), "'" + str + "'", 'primitive string shows as such');
|
||||||
|
t.equal(inspect(str, { quoteStyle: 'single' }), "'" + str + "'", 'primitive string shows as such, single quoted');
|
||||||
|
t.equal(inspect(str, { quoteStyle: 'double' }), '"' + str + '"', 'primitive string shows as such, double quoted');
|
||||||
|
t.equal(inspect(Object(str)), 'Object(' + inspect(str) + ')', 'String object shows as such');
|
||||||
|
t.equal(inspect(Object(str), { quoteStyle: 'single' }), 'Object(' + inspect(str, { quoteStyle: 'single' }) + ')', 'String object shows as such, single quoted');
|
||||||
|
t.equal(inspect(Object(str), { quoteStyle: 'double' }), 'Object(' + inspect(str, { quoteStyle: 'double' }) + ')', 'String object shows as such, double quoted');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Numbers', function (t) {
|
||||||
|
var num = 42;
|
||||||
|
|
||||||
|
t.equal(inspect(num), String(num), 'primitive number shows as such');
|
||||||
|
t.equal(inspect(Object(num)), 'Object(' + inspect(num) + ')', 'Number object shows as such');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Booleans', function (t) {
|
||||||
|
t.equal(inspect(true), String(true), 'primitive true shows as such');
|
||||||
|
t.equal(inspect(Object(true)), 'Object(' + inspect(true) + ')', 'Boolean object true shows as such');
|
||||||
|
|
||||||
|
t.equal(inspect(false), String(false), 'primitive false shows as such');
|
||||||
|
t.equal(inspect(Object(false)), 'Object(' + inspect(false) + ')', 'Boolean false object shows as such');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Date', function (t) {
|
||||||
|
var now = new Date();
|
||||||
|
t.equal(inspect(now), String(now), 'Date shows properly');
|
||||||
|
t.equal(inspect(new Date(NaN)), 'Invalid Date', 'Invalid Date shows properly');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('RegExps', function (t) {
|
||||||
|
t.equal(inspect(/a/g), '/a/g', 'regex shows properly');
|
||||||
|
t.equal(inspect(new RegExp('abc', 'i')), '/abc/i', 'new RegExp shows properly');
|
||||||
|
|
||||||
|
var match = 'abc abc'.match(/[ab]+/);
|
||||||
|
delete match.groups; // for node < 10
|
||||||
|
t.equal(inspect(match), '[ \'ab\', index: 0, input: \'abc abc\' ]', 'RegExp match object shows properly');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Proxies', { skip: typeof Proxy !== 'function' || !hasToStringTag }, function (t) {
|
||||||
|
var target = { proxy: true };
|
||||||
|
var fake = new Proxy(target, { has: function () { return false; } });
|
||||||
|
|
||||||
|
// needed to work around a weird difference in node v6.0 - v6.4 where non-present properties are not logged
|
||||||
|
var isNode60 = semver.satisfies(process.version, '6.0 - 6.4');
|
||||||
|
|
||||||
|
forEach([
|
||||||
|
'Boolean',
|
||||||
|
'Number',
|
||||||
|
'String',
|
||||||
|
'Symbol',
|
||||||
|
'Date'
|
||||||
|
], function (tag) {
|
||||||
|
target[Symbol.toStringTag] = tag;
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
inspect(fake),
|
||||||
|
'{ ' + (isNode60 ? '' : 'proxy: true, ') + '[Symbol(Symbol.toStringTag)]: \'' + tag + '\' }',
|
||||||
|
'Proxy for + ' + tag + ' shows as the target, which has no slots'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fakers', { skip: !hasToStringTag }, function (t) {
|
||||||
|
var target = { proxy: false };
|
||||||
|
|
||||||
|
forEach([
|
||||||
|
'Boolean',
|
||||||
|
'Number',
|
||||||
|
'String',
|
||||||
|
'Symbol',
|
||||||
|
'Date'
|
||||||
|
], function (tag) {
|
||||||
|
target[Symbol.toStringTag] = tag;
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
inspect(target),
|
||||||
|
'{ proxy: false, [Symbol(Symbol.toStringTag)]: \'' + tag + '\' }',
|
||||||
|
'Object pretending to be ' + tag + ' does not trick us'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Jordan Harband
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fill = require('fill-range');
|
||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
const compile = (ast, options = {}) => {
|
||||||
|
const walk = (node, parent = {}) => {
|
||||||
|
const invalidBlock = utils.isInvalidBrace(parent);
|
||||||
|
const invalidNode = node.invalid === true && options.escapeInvalid === true;
|
||||||
|
const invalid = invalidBlock === true || invalidNode === true;
|
||||||
|
const prefix = options.escapeInvalid === true ? '\\' : '';
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
if (node.isOpen === true) {
|
||||||
|
return prefix + node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.isClose === true) {
|
||||||
|
console.log('node.isClose', prefix, node.value);
|
||||||
|
return prefix + node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'open') {
|
||||||
|
return invalid ? prefix + node.value : '(';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'close') {
|
||||||
|
return invalid ? prefix + node.value : ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'comma') {
|
||||||
|
return node.prev.type === 'comma' ? '' : invalid ? node.value : '|';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.value) {
|
||||||
|
return node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodes && node.ranges > 0) {
|
||||||
|
const args = utils.reduce(node.nodes);
|
||||||
|
const range = fill(...args, { ...options, wrap: false, toRegex: true, strictZeros: true });
|
||||||
|
|
||||||
|
if (range.length !== 0) {
|
||||||
|
return args.length > 1 && range.length > 1 ? `(${range})` : range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodes) {
|
||||||
|
for (const child of node.nodes) {
|
||||||
|
output += walk(child, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
return walk(ast);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = compile;
|
||||||
@ -0,0 +1,210 @@
|
|||||||
|
/*!
|
||||||
|
* body-parser
|
||||||
|
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var createError = require('http-errors')
|
||||||
|
var getBody = require('raw-body')
|
||||||
|
var iconv = require('iconv-lite')
|
||||||
|
var onFinished = require('on-finished')
|
||||||
|
var zlib = require('node:zlib')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = read
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a request into a buffer and parse.
|
||||||
|
*
|
||||||
|
* @param {object} req
|
||||||
|
* @param {object} res
|
||||||
|
* @param {function} next
|
||||||
|
* @param {function} parse
|
||||||
|
* @param {function} debug
|
||||||
|
* @param {object} options
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function read (req, res, next, parse, debug, options) {
|
||||||
|
var length
|
||||||
|
var opts = options
|
||||||
|
var stream
|
||||||
|
|
||||||
|
// read options
|
||||||
|
var encoding = opts.encoding !== null
|
||||||
|
? opts.encoding
|
||||||
|
: null
|
||||||
|
var verify = opts.verify
|
||||||
|
|
||||||
|
try {
|
||||||
|
// get the content stream
|
||||||
|
stream = contentstream(req, debug, opts.inflate)
|
||||||
|
length = stream.length
|
||||||
|
stream.length = undefined
|
||||||
|
} catch (err) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set raw-body options
|
||||||
|
opts.length = length
|
||||||
|
opts.encoding = verify
|
||||||
|
? null
|
||||||
|
: encoding
|
||||||
|
|
||||||
|
// assert charset is supported
|
||||||
|
if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
|
||||||
|
return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
|
||||||
|
charset: encoding.toLowerCase(),
|
||||||
|
type: 'charset.unsupported'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// read body
|
||||||
|
debug('read body')
|
||||||
|
getBody(stream, opts, function (error, body) {
|
||||||
|
if (error) {
|
||||||
|
var _error
|
||||||
|
|
||||||
|
if (error.type === 'encoding.unsupported') {
|
||||||
|
// echo back charset
|
||||||
|
_error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
|
||||||
|
charset: encoding.toLowerCase(),
|
||||||
|
type: 'charset.unsupported'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// set status code on error
|
||||||
|
_error = createError(400, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpipe from stream and destroy
|
||||||
|
if (stream !== req) {
|
||||||
|
req.unpipe()
|
||||||
|
stream.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// read off entire request
|
||||||
|
dump(req, function onfinished () {
|
||||||
|
next(createError(400, _error))
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify
|
||||||
|
if (verify) {
|
||||||
|
try {
|
||||||
|
debug('verify body')
|
||||||
|
verify(req, res, body, encoding)
|
||||||
|
} catch (err) {
|
||||||
|
next(createError(403, err, {
|
||||||
|
body: body,
|
||||||
|
type: err.type || 'entity.verify.failed'
|
||||||
|
}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse
|
||||||
|
var str = body
|
||||||
|
try {
|
||||||
|
debug('parse body')
|
||||||
|
str = typeof body !== 'string' && encoding !== null
|
||||||
|
? iconv.decode(body, encoding)
|
||||||
|
: body
|
||||||
|
req.body = parse(str, encoding)
|
||||||
|
} catch (err) {
|
||||||
|
next(createError(400, err, {
|
||||||
|
body: str,
|
||||||
|
type: err.type || 'entity.parse.failed'
|
||||||
|
}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the content stream of the request.
|
||||||
|
*
|
||||||
|
* @param {object} req
|
||||||
|
* @param {function} debug
|
||||||
|
* @param {boolean} [inflate=true]
|
||||||
|
* @return {object}
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function contentstream (req, debug, inflate) {
|
||||||
|
var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
|
||||||
|
var length = req.headers['content-length']
|
||||||
|
|
||||||
|
debug('content-encoding "%s"', encoding)
|
||||||
|
|
||||||
|
if (inflate === false && encoding !== 'identity') {
|
||||||
|
throw createError(415, 'content encoding unsupported', {
|
||||||
|
encoding: encoding,
|
||||||
|
type: 'encoding.unsupported'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoding === 'identity') {
|
||||||
|
req.length = length
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = createDecompressionStream(encoding, debug)
|
||||||
|
req.pipe(stream)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a decompression stream for the given encoding.
|
||||||
|
* @param {string} encoding
|
||||||
|
* @param {function} debug
|
||||||
|
* @return {object}
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
function createDecompressionStream (encoding, debug) {
|
||||||
|
switch (encoding) {
|
||||||
|
case 'deflate':
|
||||||
|
debug('inflate body')
|
||||||
|
return zlib.createInflate()
|
||||||
|
case 'gzip':
|
||||||
|
debug('gunzip body')
|
||||||
|
return zlib.createGunzip()
|
||||||
|
case 'br':
|
||||||
|
debug('brotli decompress body')
|
||||||
|
return zlib.createBrotliDecompress()
|
||||||
|
default:
|
||||||
|
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
|
||||||
|
encoding: encoding,
|
||||||
|
type: 'encoding.unsupported'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the contents of a request.
|
||||||
|
*
|
||||||
|
* @param {object} req
|
||||||
|
* @param {function} callback
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function dump (req, callback) {
|
||||||
|
if (onFinished.isFinished(req)) {
|
||||||
|
callback(null)
|
||||||
|
} else {
|
||||||
|
onFinished(req, callback)
|
||||||
|
req.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
cat: /proc/4087/stat: No such file or directory
|
||||||
|
cat: /proc/4088/stat: No such file or directory
|
||||||
|
1 (init) S 0 1 1 0 -1 4210944 9227 55994 29 319 7 5 68 16 20 0 1 0 1286281 33660928 855 18446744073709551615 1 1 0 0 0 0 0 4096 536962595 0 0 0 17 4 0 0 3 0 0 0 0 0 0 0 0 0 0
|
||||||
|
1032 (ntpd) S 1 1032 1032 0 -1 4211008 178 0 1 0 0 0 0 0 20 0 1 0 1287033 25743360 1058 18446744073709551615 1 1 0 0 0 0 0 4096 27207 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
126 (irqbalance) S 1 126 126 0 -1 1077952832 1217 0 0 0 1 6 0 0 20 0 1 0 1286749 20189184 647 18446744073709551615 1 1 0 0 0 0 0 0 3 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
181 (mysqld) S 1 181 181 0 -1 4210944 6399 0 46 0 8 6 0 0 20 0 22 0 1286761 748453888 14476 18446744073709551615 1 1 0 0 0 0 552967 4096 26345 0 0 0 17 4 0 0 10 0 0 0 0 0 0 0 0 0 0
|
||||||
|
194 (memcached) S 1 187 187 0 -1 4210944 252 0 4 0 0 0 0 0 20 0 6 0 1286766 333221888 648 18446744073709551615 1 1 0 0 0 0 0 4096 2 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
243 (dbus-daemon) S 1 243 243 0 -1 4211008 67 0 0 0 0 0 0 0 20 0 1 0 1286779 40087552 598 18446744073709551615 1 1 0 0 0 0 0 0 16385 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
254 (rsyslogd) S 1 254 254 0 -1 4211008 107 0 0 0 2 2 0 0 20 0 3 0 1286782 186601472 696 18446744073709551615 1 1 0 0 0 0 0 16781830 1133601 0 0 0 17 5 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
265 (systemd-logind) S 1 265 265 0 -1 4210944 276 0 2 0 0 0 0 0 20 0 1 0 1286786 35880960 720 18446744073709551615 1 1 0 0 0 0 0 0 0 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
333 (postgres) S 1 303 303 0 -1 4210688 3169 3466 15 18 0 1 1 1 20 0 1 0 1286817 156073984 5002 18446744073709551615 1 1 0 0 0 0 0 19935232 84487 0 0 0 17 5 0 0 1 0 0 0 0 0 0 0 0 0 0
|
||||||
|
359 (postgres) S 333 359 359 0 -1 4210752 90 0 0 0 0 0 0 0 20 0 1 0 1286822 156073984 827 18446744073709551615 1 1 0 0 0 0 0 16805888 2567 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
360 (postgres) S 333 360 360 0 -1 4210752 119 0 0 0 0 0 0 0 20 0 1 0 1286822 156073984 827 18446744073709551615 1 1 0 0 0 0 0 16791554 16901 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
361 (postgres) S 333 361 361 0 -1 4210752 87 0 0 0 0 0 0 0 20 0 1 0 1286822 156073984 827 18446744073709551615 1 1 0 0 0 0 0 16791552 16903 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
362 (postgres) S 333 362 362 0 -1 4210752 292 0 3 0 0 0 0 0 20 0 1 0 1286822 156930048 1373 18446744073709551615 1 1 0 0 0 0 0 19927040 27271 0 0 0 17 5 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
363 (postgres) S 333 363 363 0 -1 4210752 82 0 0 0 0 0 0 0 20 0 1 0 1286822 115924992 887 18446744073709551615 1 1 0 0 0 0 0 16808450 5 0 0 0 17 5 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
4050 (npm) S 50 50 50 34817 50 4210688 5109 0 0 0 36 3 0 0 20 0 10 0 1292968 738025472 10051 18446744073709551615 4194304 33165900 140723623956256 0 0 0 0 4096 134300162 0 0 0 17 4 0 0 0 0 0 35263056 35370992 48369664 140723623964237 140723623964294 140723623964294 140723623968712 0
|
||||||
|
4060 (sh) S 4050 50 50 34817 50 4210688 121 0 0 0 0 0 0 0 20 0 1 0 1293007 4579328 174 18446744073709551615 94347643936768 94347644049516 140735136055088 0 0 0 0 0 65538 1 0 0 17 5 0 0 0 0 0 94347646148008 94347646153216 94347660038144 140735136063095 140735136063129 140735136063129 140735136071664 0
|
||||||
|
4061 (node) S 4060 50 50 34817 50 4210688 6501 0 0 0 42 2 0 0 20 0 6 0 1293008 705769472 10211 18446744073709551615 4194304 33165900 140730532686288 0 0 0 0 4096 2072111671 0 0 0 17 5 0 0 0 0 0 35263056 35370992 45867008 140730532695579 140730532695657 140730532695657 140730532704200 0
|
||||||
|
4067 (node) S 4061 50 50 34817 50 4210688 6746 221 0 0 38 3 0 0 20 0 10 0 1293051 738910208 10527 18446744073709551615 4194304 33165900 140724824971632 0 0 0 0 4096 2072111671 0 0 0 17 4 0 0 0 0 0 35263056 35370992 68595712 140724824980995 140724824981063 140724824981063 140724824989640 0
|
||||||
|
4079 (sh) S 4067 50 50 34817 50 4210688 118 0 0 0 0 0 0 0 20 0 1 0 1293092 4579328 194 18446744073709551615 94573702131712 94573702244460 140724712357120 0 0 0 0 0 65538 1 0 0 17 4 0 0 0 0 0 94573704342952 94573704348160 94573718511616 140724712361487 140724712361583 140724712361583 140724712370160 0
|
||||||
|
4080 (node) S 4079 50 50 34817 50 4210688 2428 0 0 0 8 1 0 0 20 0 6 0 1293093 693059584 7251 18446744073709551615 4194304 33165900 140726023392816 0 0 0 0 4096 134234626 0 0 0 17 5 0 0 0 0 0 35263056 35370992 55226368 140726023396847 140726023396935 140726023396935 140726023405512 0
|
||||||
|
4086 (sh) S 4067 50 50 34817 50 4210688 131 244 0 0 0 0 0 0 20 0 1 0 1293143 4579328 200 18446744073709551615 94347550273536 94347550386284 140737219399136 0 0 0 0 0 65538 1 0 0 17 5 0 0 0 0 0 94347552484776 94347552489984 94347554299904 140737219403308 140737219403375 140737219403375 140737219411952 0
|
||||||
|
4089 (xargs) S 4086 50 50 34817 50 4210688 333 1924 0 0 0 0 0 0 20 0 1 0 1293143 17600512 477 18446744073709551615 4194304 4232732 140721633759248 0 0 0 0 0 0 1 0 0 17 5 0 0 0 0 0 6331920 6332980 32182272 140721633762891 140721633762920 140721633762920 140721633771497 0
|
||||||
|
50 (bash) S 0 50 50 34817 50 4210944 43914 1032463 9 705 44 21 4213 818 20 0 1 0 1286336 42266624 3599 18446744073709551615 4194304 5173404 140732749083280 0 0 0 65536 4 1132560123 1 0 0 17 4 0 0 410 0 0 7273968 7310504 21196800 140732749086490 140732749086517 140732749086517 140732749086702 0
|
||||||
|
79 (acpid) S 1 79 79 0 -1 4210752 46 0 0 0 0 0 0 0 20 0 1 0 1286717 4493312 407 18446744073709551615 1 1 0 0 0 0 0 4096 16391 0 0 0 17 5 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
83 (sshd) S 1 83 83 0 -1 4210944 354 0 27 0 0 0 0 0 20 0 1 0 1286718 62873600 1290 18446744073709551615 1 1 0 0 0 0 0 4096 81925 0 0 0 17 4 0 0 30 0 0 0 0 0 0 0 0 0 0
|
||||||
|
94 (cron) S 1 94 94 0 -1 1077952576 103 449 0 1 0 0 0 0 20 0 1 0 1286743 24240128 559 18446744073709551615 1 1 0 0 0 0 0 0 65537 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
95 (atd) S 1 95 95 0 -1 1077952576 28 0 0 0 0 0 0 0 20 0 1 0 1286743 19615744 41 18446744073709551615 1 1 0 0 0 0 0 0 81923 0 0 0 17 4 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var test = require('tape');
|
||||||
|
|
||||||
|
var getSideChannelWeakMap = require('../');
|
||||||
|
|
||||||
|
test('getSideChannelMap', { skip: typeof WeakMap !== 'function' && typeof Map !== 'function' }, function (t) {
|
||||||
|
var getSideChannel = getSideChannelWeakMap || function () {
|
||||||
|
throw new EvalError('should never happen');
|
||||||
|
};
|
||||||
|
|
||||||
|
t.test('export', function (st) {
|
||||||
|
st.equal(typeof getSideChannel, 'function', 'is a function');
|
||||||
|
|
||||||
|
st.equal(getSideChannel.length, 0, 'takes no arguments');
|
||||||
|
|
||||||
|
var channel = getSideChannel();
|
||||||
|
st.ok(channel, 'is truthy');
|
||||||
|
st.equal(typeof channel, 'object', 'is an object');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('assert', function (st) {
|
||||||
|
var channel = getSideChannel();
|
||||||
|
st['throws'](
|
||||||
|
function () { channel.assert({}); },
|
||||||
|
TypeError,
|
||||||
|
'nonexistent value throws'
|
||||||
|
);
|
||||||
|
|
||||||
|
var o = {};
|
||||||
|
channel.set(o, 'data');
|
||||||
|
st.doesNotThrow(function () { channel.assert(o); }, 'existent value noops');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('has', function (st) {
|
||||||
|
var channel = getSideChannel();
|
||||||
|
/** @type {unknown[]} */ var o = [];
|
||||||
|
|
||||||
|
st.equal(channel.has(o), false, 'nonexistent value yields false');
|
||||||
|
|
||||||
|
channel.set(o, 'foo');
|
||||||
|
st.equal(channel.has(o), true, 'existent value yields true');
|
||||||
|
|
||||||
|
st.equal(channel.has('abc'), false, 'non object value non existent yields false');
|
||||||
|
|
||||||
|
channel.set('abc', 'foo');
|
||||||
|
st.equal(channel.has('abc'), true, 'non object value that exists yields true');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('get', function (st) {
|
||||||
|
var channel = getSideChannel();
|
||||||
|
var o = {};
|
||||||
|
st.equal(channel.get(o), undefined, 'nonexistent value yields undefined');
|
||||||
|
|
||||||
|
var data = {};
|
||||||
|
channel.set(o, data);
|
||||||
|
st.equal(channel.get(o), data, '"get" yields data set by "set"');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('set', function (st) {
|
||||||
|
var channel = getSideChannel();
|
||||||
|
var o = function () {};
|
||||||
|
st.equal(channel.get(o), undefined, 'value not set');
|
||||||
|
|
||||||
|
channel.set(o, 42);
|
||||||
|
st.equal(channel.get(o), 42, 'value was set');
|
||||||
|
|
||||||
|
channel.set(o, Infinity);
|
||||||
|
st.equal(channel.get(o), Infinity, 'value was set again');
|
||||||
|
|
||||||
|
var o2 = {};
|
||||||
|
channel.set(o2, 17);
|
||||||
|
st.equal(channel.get(o), Infinity, 'o is not modified');
|
||||||
|
st.equal(channel.get(o2), 17, 'o2 is set');
|
||||||
|
|
||||||
|
channel.set(o, 14);
|
||||||
|
st.equal(channel.get(o), 14, 'o is modified');
|
||||||
|
st.equal(channel.get(o2), 17, 'o2 is not modified');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('delete', function (st) {
|
||||||
|
var channel = getSideChannel();
|
||||||
|
var o = {};
|
||||||
|
st.equal(channel['delete']({}), false, 'nonexistent value yields false');
|
||||||
|
|
||||||
|
channel.set(o, 42);
|
||||||
|
st.equal(channel.has(o), true, 'value is set');
|
||||||
|
|
||||||
|
st.equal(channel['delete']({}), false, 'nonexistent value still yields false');
|
||||||
|
|
||||||
|
st.equal(channel['delete'](o), true, 'deleted value yields true');
|
||||||
|
|
||||||
|
st.equal(channel.has(o), false, 'value is no longer set');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getSideChannelMap, no WeakMaps and/or Maps', { skip: typeof WeakMap === 'function' || typeof Map === 'function' }, function (t) {
|
||||||
|
t.equal(getSideChannelWeakMap, false, 'is false');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "serve-static",
|
||||||
|
"description": "Serve static files",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "expressjs/serve-static",
|
||||||
|
"dependencies": {
|
||||||
|
"encodeurl": "^2.0.0",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
|
"parseurl": "^1.3.3",
|
||||||
|
"send": "^1.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "7.32.0",
|
||||||
|
"eslint-config-standard": "14.1.1",
|
||||||
|
"eslint-plugin-import": "2.25.4",
|
||||||
|
"eslint-plugin-markdown": "2.2.1",
|
||||||
|
"eslint-plugin-node": "11.1.0",
|
||||||
|
"eslint-plugin-promise": "5.2.0",
|
||||||
|
"eslint-plugin-standard": "4.1.0",
|
||||||
|
"mocha": "^10.7.0",
|
||||||
|
"nyc": "^17.0.0",
|
||||||
|
"supertest": "^6.3.4"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"LICENSE",
|
||||||
|
"HISTORY.md",
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"test": "mocha --reporter spec --bail --check-leaks test/",
|
||||||
|
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
|
||||||
|
"test-cov": "nyc --reporter=html --reporter=text npm test",
|
||||||
|
"version": "node scripts/version-history.js && git add HISTORY.md"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,271 @@
|
|||||||
|
var test = require('tape');
|
||||||
|
var forEach = require('for-each');
|
||||||
|
|
||||||
|
var inspect = require('../');
|
||||||
|
|
||||||
|
test('bad indent options', function (t) {
|
||||||
|
forEach([
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
-1,
|
||||||
|
1.2,
|
||||||
|
Infinity,
|
||||||
|
-Infinity,
|
||||||
|
NaN
|
||||||
|
], function (indent) {
|
||||||
|
t['throws'](
|
||||||
|
function () { inspect('', { indent: indent }); },
|
||||||
|
TypeError,
|
||||||
|
inspect(indent) + ' is invalid'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('simple object with indent', function (t) {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
var obj = { a: 1, b: 2 };
|
||||||
|
|
||||||
|
var expectedSpaces = [
|
||||||
|
'{',
|
||||||
|
' a: 1,',
|
||||||
|
' b: 2',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var expectedTabs = [
|
||||||
|
'{',
|
||||||
|
' a: 1,',
|
||||||
|
' b: 2',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
t.equal(inspect(obj, { indent: 2 }), expectedSpaces, 'two');
|
||||||
|
t.equal(inspect(obj, { indent: '\t' }), expectedTabs, 'tabs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('two deep object with indent', function (t) {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
var obj = { a: 1, b: { c: 3, d: 4 } };
|
||||||
|
|
||||||
|
var expectedSpaces = [
|
||||||
|
'{',
|
||||||
|
' a: 1,',
|
||||||
|
' b: {',
|
||||||
|
' c: 3,',
|
||||||
|
' d: 4',
|
||||||
|
' }',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var expectedTabs = [
|
||||||
|
'{',
|
||||||
|
' a: 1,',
|
||||||
|
' b: {',
|
||||||
|
' c: 3,',
|
||||||
|
' d: 4',
|
||||||
|
' }',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
t.equal(inspect(obj, { indent: 2 }), expectedSpaces, 'two');
|
||||||
|
t.equal(inspect(obj, { indent: '\t' }), expectedTabs, 'tabs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('simple array with all single line elements', function (t) {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
var obj = [1, 2, 3, 'asdf\nsdf'];
|
||||||
|
|
||||||
|
var expected = '[ 1, 2, 3, \'asdf\\nsdf\' ]';
|
||||||
|
|
||||||
|
t.equal(inspect(obj, { indent: 2 }), expected, 'two');
|
||||||
|
t.equal(inspect(obj, { indent: '\t' }), expected, 'tabs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('array with complex elements', function (t) {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
var obj = [1, { a: 1, b: { c: 1 } }, 'asdf\nsdf'];
|
||||||
|
|
||||||
|
var expectedSpaces = [
|
||||||
|
'[',
|
||||||
|
' 1,',
|
||||||
|
' {',
|
||||||
|
' a: 1,',
|
||||||
|
' b: {',
|
||||||
|
' c: 1',
|
||||||
|
' }',
|
||||||
|
' },',
|
||||||
|
' \'asdf\\nsdf\'',
|
||||||
|
']'
|
||||||
|
].join('\n');
|
||||||
|
var expectedTabs = [
|
||||||
|
'[',
|
||||||
|
' 1,',
|
||||||
|
' {',
|
||||||
|
' a: 1,',
|
||||||
|
' b: {',
|
||||||
|
' c: 1',
|
||||||
|
' }',
|
||||||
|
' },',
|
||||||
|
' \'asdf\\nsdf\'',
|
||||||
|
']'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
t.equal(inspect(obj, { indent: 2 }), expectedSpaces, 'two');
|
||||||
|
t.equal(inspect(obj, { indent: '\t' }), expectedTabs, 'tabs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('values', function (t) {
|
||||||
|
t.plan(2);
|
||||||
|
var obj = [{}, [], { 'a-b': 5 }];
|
||||||
|
|
||||||
|
var expectedSpaces = [
|
||||||
|
'[',
|
||||||
|
' {},',
|
||||||
|
' [],',
|
||||||
|
' {',
|
||||||
|
' \'a-b\': 5',
|
||||||
|
' }',
|
||||||
|
']'
|
||||||
|
].join('\n');
|
||||||
|
var expectedTabs = [
|
||||||
|
'[',
|
||||||
|
' {},',
|
||||||
|
' [],',
|
||||||
|
' {',
|
||||||
|
' \'a-b\': 5',
|
||||||
|
' }',
|
||||||
|
']'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
t.equal(inspect(obj, { indent: 2 }), expectedSpaces, 'two');
|
||||||
|
t.equal(inspect(obj, { indent: '\t' }), expectedTabs, 'tabs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Map', { skip: typeof Map !== 'function' }, function (t) {
|
||||||
|
var map = new Map();
|
||||||
|
map.set({ a: 1 }, ['b']);
|
||||||
|
map.set(3, NaN);
|
||||||
|
|
||||||
|
var expectedStringSpaces = [
|
||||||
|
'Map (2) {',
|
||||||
|
' { a: 1 } => [ \'b\' ],',
|
||||||
|
' 3 => NaN',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var expectedStringTabs = [
|
||||||
|
'Map (2) {',
|
||||||
|
' { a: 1 } => [ \'b\' ],',
|
||||||
|
' 3 => NaN',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var expectedStringTabsDoubleQuotes = [
|
||||||
|
'Map (2) {',
|
||||||
|
' { a: 1 } => [ "b" ],',
|
||||||
|
' 3 => NaN',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
inspect(map, { indent: 2 }),
|
||||||
|
expectedStringSpaces,
|
||||||
|
'Map keys are not indented (two)'
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
inspect(map, { indent: '\t' }),
|
||||||
|
expectedStringTabs,
|
||||||
|
'Map keys are not indented (tabs)'
|
||||||
|
);
|
||||||
|
t.equal(
|
||||||
|
inspect(map, { indent: '\t', quoteStyle: 'double' }),
|
||||||
|
expectedStringTabsDoubleQuotes,
|
||||||
|
'Map keys are not indented (tabs + double quotes)'
|
||||||
|
);
|
||||||
|
|
||||||
|
t.equal(inspect(new Map(), { indent: 2 }), 'Map (0) {}', 'empty Map should show as empty (two)');
|
||||||
|
t.equal(inspect(new Map(), { indent: '\t' }), 'Map (0) {}', 'empty Map should show as empty (tabs)');
|
||||||
|
|
||||||
|
var nestedMap = new Map();
|
||||||
|
nestedMap.set(nestedMap, map);
|
||||||
|
var expectedNestedSpaces = [
|
||||||
|
'Map (1) {',
|
||||||
|
' [Circular] => Map (2) {',
|
||||||
|
' { a: 1 } => [ \'b\' ],',
|
||||||
|
' 3 => NaN',
|
||||||
|
' }',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var expectedNestedTabs = [
|
||||||
|
'Map (1) {',
|
||||||
|
' [Circular] => Map (2) {',
|
||||||
|
' { a: 1 } => [ \'b\' ],',
|
||||||
|
' 3 => NaN',
|
||||||
|
' }',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
t.equal(inspect(nestedMap, { indent: 2 }), expectedNestedSpaces, 'Map containing a Map should work (two)');
|
||||||
|
t.equal(inspect(nestedMap, { indent: '\t' }), expectedNestedTabs, 'Map containing a Map should work (tabs)');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Set', { skip: typeof Set !== 'function' }, function (t) {
|
||||||
|
var set = new Set();
|
||||||
|
set.add({ a: 1 });
|
||||||
|
set.add(['b']);
|
||||||
|
var expectedStringSpaces = [
|
||||||
|
'Set (2) {',
|
||||||
|
' {',
|
||||||
|
' a: 1',
|
||||||
|
' },',
|
||||||
|
' [ \'b\' ]',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var expectedStringTabs = [
|
||||||
|
'Set (2) {',
|
||||||
|
' {',
|
||||||
|
' a: 1',
|
||||||
|
' },',
|
||||||
|
' [ \'b\' ]',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
t.equal(inspect(set, { indent: 2 }), expectedStringSpaces, 'new Set([{ a: 1 }, ["b"]]) should show size and contents (two)');
|
||||||
|
t.equal(inspect(set, { indent: '\t' }), expectedStringTabs, 'new Set([{ a: 1 }, ["b"]]) should show size and contents (tabs)');
|
||||||
|
|
||||||
|
t.equal(inspect(new Set(), { indent: 2 }), 'Set (0) {}', 'empty Set should show as empty (two)');
|
||||||
|
t.equal(inspect(new Set(), { indent: '\t' }), 'Set (0) {}', 'empty Set should show as empty (tabs)');
|
||||||
|
|
||||||
|
var nestedSet = new Set();
|
||||||
|
nestedSet.add(set);
|
||||||
|
nestedSet.add(nestedSet);
|
||||||
|
var expectedNestedSpaces = [
|
||||||
|
'Set (2) {',
|
||||||
|
' Set (2) {',
|
||||||
|
' {',
|
||||||
|
' a: 1',
|
||||||
|
' },',
|
||||||
|
' [ \'b\' ]',
|
||||||
|
' },',
|
||||||
|
' [Circular]',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var expectedNestedTabs = [
|
||||||
|
'Set (2) {',
|
||||||
|
' Set (2) {',
|
||||||
|
' {',
|
||||||
|
' a: 1',
|
||||||
|
' },',
|
||||||
|
' [ \'b\' ]',
|
||||||
|
' },',
|
||||||
|
' [Circular]',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
t.equal(inspect(nestedSet, { indent: 2 }), expectedNestedSpaces, 'Set containing a Set should work (two)');
|
||||||
|
t.equal(inspect(nestedSet, { indent: '\t' }), expectedNestedTabs, 'Set containing a Set should work (tabs)');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
@ -0,0 +1,416 @@
|
|||||||
|
# router
|
||||||
|
|
||||||
|
[![NPM Version][npm-image]][npm-url]
|
||||||
|
[![NPM Downloads][downloads-image]][downloads-url]
|
||||||
|
[![Node.js Version][node-version-image]][node-version-url]
|
||||||
|
[![Build Status][ci-image]][ci-url]
|
||||||
|
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||||
|
|
||||||
|
Simple middleware-style router
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||||
|
[npm registry](https://www.npmjs.com/). Installation is done using the
|
||||||
|
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install router
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
var finalhandler = require('finalhandler')
|
||||||
|
var http = require('http')
|
||||||
|
var Router = require('router')
|
||||||
|
|
||||||
|
var router = Router()
|
||||||
|
router.get('/', function (req, res) {
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end('Hello World!')
|
||||||
|
})
|
||||||
|
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
router(req, res, finalhandler(req, res))
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(3000)
|
||||||
|
```
|
||||||
|
|
||||||
|
This module is currently an extracted version from the Express project,
|
||||||
|
but with the main change being it can be used with a plain `http.createServer`
|
||||||
|
object or other web frameworks by removing Express-specific API calls.
|
||||||
|
|
||||||
|
## Router(options)
|
||||||
|
|
||||||
|
Options
|
||||||
|
|
||||||
|
- `strict` - When `false` trailing slashes are optional (default: `false`)
|
||||||
|
- `caseSensitive` - When `true` the routing will be case sensitive. (default: `false`)
|
||||||
|
- `mergeParams` - When `true` any `req.params` passed to the router will be
|
||||||
|
merged into the router's `req.params`. (default: `false`) ([example](#example-using-mergeparams))
|
||||||
|
|
||||||
|
Returns a function with the signature `router(req, res, callback)` where
|
||||||
|
`callback([err])` must be provided to handle errors and fall-through from
|
||||||
|
not handling requests.
|
||||||
|
|
||||||
|
### router.use([path], ...middleware)
|
||||||
|
|
||||||
|
Use the given [middleware function](#middleware) for all http methods on the
|
||||||
|
given `path`, defaulting to the root path.
|
||||||
|
|
||||||
|
`router` does not automatically see `use` as a handler. As such, it will not
|
||||||
|
consider it one for handling `OPTIONS` requests.
|
||||||
|
|
||||||
|
* Note: If a `path` is specified, that `path` is stripped from the start of
|
||||||
|
`req.url`.
|
||||||
|
|
||||||
|
<!-- eslint-disable no-undef -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
router.use(function (req, res, next) {
|
||||||
|
// do your things
|
||||||
|
|
||||||
|
// continue to the next middleware
|
||||||
|
// the request will stall if this is not called
|
||||||
|
next()
|
||||||
|
|
||||||
|
// note: you should NOT call `next` if you have begun writing to the response
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
[Middleware](#middleware) can themselves use `next('router')` at any time to
|
||||||
|
exit the current router instance completely, invoking the top-level callback.
|
||||||
|
|
||||||
|
### router\[method](path, ...[middleware], handler)
|
||||||
|
|
||||||
|
The [http methods](https://github.com/jshttp/methods/blob/master/index.js) provide
|
||||||
|
the routing functionality in `router`.
|
||||||
|
|
||||||
|
Method middleware and handlers follow usual [middleware](#middleware) behavior,
|
||||||
|
except they will only be called when the method and path match the request.
|
||||||
|
|
||||||
|
<!-- eslint-disable no-undef -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
// handle a `GET` request
|
||||||
|
router.get('/', function (req, res) {
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end('Hello World!')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
[Middleware](#middleware) given before the handler have one additional trick,
|
||||||
|
they may invoke `next('route')`. Calling `next('route')` bypasses the remaining
|
||||||
|
middleware and the handler mounted for this route, passing the request to the
|
||||||
|
next route suitable for handling this request.
|
||||||
|
|
||||||
|
Route handlers and middleware can themselves use `next('router')` at any time
|
||||||
|
to exit the current router instance completely, invoking the top-level callback.
|
||||||
|
|
||||||
|
### router.param(name, param_middleware)
|
||||||
|
|
||||||
|
Maps the specified path parameter `name` to a specialized param-capturing middleware.
|
||||||
|
|
||||||
|
This function positions the middleware in the same stack as `.use`.
|
||||||
|
|
||||||
|
The function can optionally return a `Promise` object. If a `Promise` object
|
||||||
|
is returned from the function, the router will attach an `onRejected` callback
|
||||||
|
using `.then`. If the promise is rejected, `next` will be called with the
|
||||||
|
rejected value, or an error if the value is falsy.
|
||||||
|
|
||||||
|
Parameter mapping is used to provide pre-conditions to routes
|
||||||
|
which use normalized placeholders. For example a _:user_id_ parameter
|
||||||
|
could automatically load a user's information from the database without
|
||||||
|
any additional code:
|
||||||
|
|
||||||
|
<!-- eslint-disable no-undef -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
router.param('user_id', function (req, res, next, id) {
|
||||||
|
User.find(id, function (err, user) {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
} else if (!user) {
|
||||||
|
return next(new Error('failed to load user'))
|
||||||
|
}
|
||||||
|
req.user = user
|
||||||
|
|
||||||
|
// continue processing the request
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### router.route(path)
|
||||||
|
|
||||||
|
Creates an instance of a single `Route` for the given `path`.
|
||||||
|
(See `Router.Route` below)
|
||||||
|
|
||||||
|
Routes can be used to handle http `methods` with their own, optional middleware.
|
||||||
|
|
||||||
|
Using `router.route(path)` is a recommended approach to avoiding duplicate
|
||||||
|
route naming and thus typo errors.
|
||||||
|
|
||||||
|
<!-- eslint-disable no-undef, no-unused-vars -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
var api = router.route('/api/')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Router.Route(path)
|
||||||
|
|
||||||
|
Represents a single route as an instance that can be used to handle http
|
||||||
|
`methods` with it's own, optional middleware.
|
||||||
|
|
||||||
|
### route\[method](handler)
|
||||||
|
|
||||||
|
These are functions which you can directly call on a route to register a new
|
||||||
|
`handler` for the `method` on the route.
|
||||||
|
|
||||||
|
<!-- eslint-disable no-undef -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
// handle a `GET` request
|
||||||
|
var status = router.route('/status')
|
||||||
|
|
||||||
|
status.get(function (req, res) {
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end('All Systems Green!')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### route.all(handler)
|
||||||
|
|
||||||
|
Adds a handler for all HTTP methods to this route.
|
||||||
|
|
||||||
|
The handler can behave like middleware and call `next` to continue processing
|
||||||
|
rather than responding.
|
||||||
|
|
||||||
|
<!-- eslint-disable no-undef -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
router.route('/')
|
||||||
|
.all(function (req, res, next) {
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
.all(checkSomething)
|
||||||
|
.get(function (req, res) {
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end('Hello World!')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Middleware
|
||||||
|
|
||||||
|
Middleware (and method handlers) are functions that follow specific function
|
||||||
|
parameters and have defined behavior when used with `router`. The most common
|
||||||
|
format is with three parameters - "req", "res" and "next".
|
||||||
|
|
||||||
|
- `req` - This is a [HTTP incoming message](https://nodejs.org/api/http.html#http_http_incomingmessage) instance.
|
||||||
|
- `res` - This is a [HTTP server response](https://nodejs.org/api/http.html#http_class_http_serverresponse) instance.
|
||||||
|
- `next` - Calling this function that tells `router` to proceed to the next matching middleware or method handler. It accepts an error as the first argument.
|
||||||
|
|
||||||
|
The function can optionally return a `Promise` object. If a `Promise` object
|
||||||
|
is returned from the function, the router will attach an `onRejected` callback
|
||||||
|
using `.then`. If the promise is rejected, `next` will be called with the
|
||||||
|
rejected value, or an error if the value is falsy.
|
||||||
|
|
||||||
|
Middleware and method handlers can also be defined with four arguments. When
|
||||||
|
the function has four parameters defined, the first argument is an error and
|
||||||
|
subsequent arguments remain, becoming - "err", "req", "res", "next". These
|
||||||
|
functions are "error handling middleware", and can be used for handling
|
||||||
|
errors that occurred in previous handlers (E.g. from calling `next(err)`).
|
||||||
|
This is most used when you want to define arbitrary rendering of errors.
|
||||||
|
|
||||||
|
<!-- eslint-disable no-undef -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
router.get('/error_route', function (req, res, next) {
|
||||||
|
return next(new Error('Bad Request'))
|
||||||
|
})
|
||||||
|
|
||||||
|
router.use(function (err, req, res, next) {
|
||||||
|
res.end(err.message) //= > "Bad Request"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Error handling middleware will **only** be invoked when an error was given. As
|
||||||
|
long as the error is in the pipeline, normal middleware and handlers will be
|
||||||
|
bypassed - only error handling middleware will be invoked with an error.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```js
|
||||||
|
// import our modules
|
||||||
|
var http = require('http')
|
||||||
|
var Router = require('router')
|
||||||
|
var finalhandler = require('finalhandler')
|
||||||
|
var compression = require('compression')
|
||||||
|
var bodyParser = require('body-parser')
|
||||||
|
|
||||||
|
// store our message to display
|
||||||
|
var message = 'Hello World!'
|
||||||
|
|
||||||
|
// initialize the router & server and add a final callback.
|
||||||
|
var router = Router()
|
||||||
|
var server = http.createServer(function onRequest (req, res) {
|
||||||
|
router(req, res, finalhandler(req, res))
|
||||||
|
})
|
||||||
|
|
||||||
|
// use some middleware and compress all outgoing responses
|
||||||
|
router.use(compression())
|
||||||
|
|
||||||
|
// handle `GET` requests to `/message`
|
||||||
|
router.get('/message', function (req, res) {
|
||||||
|
res.statusCode = 200
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end(message + '\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
// create and mount a new router for our API
|
||||||
|
var api = Router()
|
||||||
|
router.use('/api/', api)
|
||||||
|
|
||||||
|
// add a body parsing middleware to our API
|
||||||
|
api.use(bodyParser.json())
|
||||||
|
|
||||||
|
// handle `PATCH` requests to `/api/set-message`
|
||||||
|
api.patch('/set-message', function (req, res) {
|
||||||
|
if (req.body.value) {
|
||||||
|
message = req.body.value
|
||||||
|
|
||||||
|
res.statusCode = 200
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end(message + '\n')
|
||||||
|
} else {
|
||||||
|
res.statusCode = 400
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end('Invalid API Syntax\n')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// make our http server listen to connections
|
||||||
|
server.listen(8080)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can get the message by running this command in your terminal,
|
||||||
|
or navigating to `127.0.0.1:8080` in a web browser.
|
||||||
|
```bash
|
||||||
|
curl http://127.0.0.1:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the message by sending it a `PATCH` request via this command:
|
||||||
|
```bash
|
||||||
|
curl http://127.0.0.1:8080/api/set-message -X PATCH -H "Content-Type: application/json" -d '{"value":"Cats!"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example using mergeParams
|
||||||
|
|
||||||
|
```js
|
||||||
|
var http = require('http')
|
||||||
|
var Router = require('router')
|
||||||
|
var finalhandler = require('finalhandler')
|
||||||
|
|
||||||
|
// this example is about the mergeParams option
|
||||||
|
var opts = { mergeParams: true }
|
||||||
|
|
||||||
|
// make a router with out special options
|
||||||
|
var router = Router(opts)
|
||||||
|
var server = http.createServer(function onRequest (req, res) {
|
||||||
|
// set something to be passed into the router
|
||||||
|
req.params = { type: 'kitten' }
|
||||||
|
|
||||||
|
router(req, res, finalhandler(req, res))
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/', function (req, res) {
|
||||||
|
res.statusCode = 200
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
|
||||||
|
// with respond with the the params that were passed in
|
||||||
|
res.end(req.params.type + '\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
// make another router with our options
|
||||||
|
var handler = Router(opts)
|
||||||
|
|
||||||
|
// mount our new router to a route that accepts a param
|
||||||
|
router.use('/:path', handler)
|
||||||
|
|
||||||
|
handler.get('/', function (req, res) {
|
||||||
|
res.statusCode = 200
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
|
||||||
|
// will respond with the param of the router's parent route
|
||||||
|
res.end(req.params.path + '\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
// make our http server listen to connections
|
||||||
|
server.listen(8080)
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can get the type, or what path you are requesting:
|
||||||
|
```bash
|
||||||
|
curl http://127.0.0.1:8080
|
||||||
|
> kitten
|
||||||
|
curl http://127.0.0.1:8080/such_path
|
||||||
|
> such_path
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example of advanced `.route()` usage
|
||||||
|
|
||||||
|
This example shows how to implement routes where there is a custom
|
||||||
|
handler to execute when the path matched, but no methods matched.
|
||||||
|
Without any special handling, this would be treated as just a
|
||||||
|
generic non-match by `router` (which typically results in a 404),
|
||||||
|
but with a custom handler, a `405 Method Not Allowed` can be sent.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var http = require('http')
|
||||||
|
var finalhandler = require('finalhandler')
|
||||||
|
var Router = require('router')
|
||||||
|
|
||||||
|
// create the router and server
|
||||||
|
var router = new Router()
|
||||||
|
var server = http.createServer(function onRequest (req, res) {
|
||||||
|
router(req, res, finalhandler(req, res))
|
||||||
|
})
|
||||||
|
|
||||||
|
// register a route and add all methods
|
||||||
|
router.route('/pet/:id')
|
||||||
|
.get(function (req, res) {
|
||||||
|
// this is GET /pet/:id
|
||||||
|
res.setHeader('Content-Type', 'application/json')
|
||||||
|
res.end(JSON.stringify({ name: 'tobi' }))
|
||||||
|
})
|
||||||
|
.delete(function (req, res) {
|
||||||
|
// this is DELETE /pet/:id
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
.all(function (req, res) {
|
||||||
|
// this is called for all other methods not
|
||||||
|
// defined above for /pet/:id
|
||||||
|
res.statusCode = 405
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
// make our http server listen to connections
|
||||||
|
server.listen(8080)
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[ci-image]: https://badgen.net/github/checks/pillarjs/router/master?label=ci
|
||||||
|
[ci-url]: https://github.com/pillarjs/router/actions/workflows/ci.yml
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/router.svg
|
||||||
|
[npm-url]: https://npmjs.org/package/router
|
||||||
|
[node-version-image]: https://img.shields.io/node/v/router.svg
|
||||||
|
[node-version-url]: http://nodejs.org/download/
|
||||||
|
[coveralls-image]: https://img.shields.io/coveralls/pillarjs/router/master.svg
|
||||||
|
[coveralls-url]: https://coveralls.io/r/pillarjs/router?branch=master
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/router.svg
|
||||||
|
[downloads-url]: https://npmjs.org/package/router
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "safer-buffer",
|
||||||
|
"version": "2.1.2",
|
||||||
|
"description": "Modern Buffer API polyfill without footguns",
|
||||||
|
"main": "safer.js",
|
||||||
|
"scripts": {
|
||||||
|
"browserify-test": "browserify --external tape tests.js > browserify-tests.js && tape browserify-tests.js",
|
||||||
|
"test": "standard && tape tests.js"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Nikita Skovoroda",
|
||||||
|
"email": "chalkerx@gmail.com",
|
||||||
|
"url": "https://github.com/ChALkeR"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/ChALkeR/safer-buffer.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ChALkeR/safer-buffer/issues"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"standard": "^11.0.1",
|
||||||
|
"tape": "^4.9.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"Porting-Buffer.md",
|
||||||
|
"Readme.md",
|
||||||
|
"tests.js",
|
||||||
|
"dangerous.js",
|
||||||
|
"safer.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Jordan Harband
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const compare = require('./compare')
|
||||||
|
const gte = (a, b, loose) => compare(a, b, loose) >= 0
|
||||||
|
module.exports = gte
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [ljharb]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: npm/has-symbols
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
# on-finished
|
||||||
|
|
||||||
|
[![NPM Version][npm-version-image]][npm-url]
|
||||||
|
[![NPM Downloads][npm-downloads-image]][npm-url]
|
||||||
|
[![Node.js Version][node-image]][node-url]
|
||||||
|
[![Build Status][ci-image]][ci-url]
|
||||||
|
[![Coverage Status][coveralls-image]][coveralls-url]
|
||||||
|
|
||||||
|
Execute a callback when a HTTP request closes, finishes, or errors.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||||
|
[npm registry](https://www.npmjs.com/). Installation is done using the
|
||||||
|
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ npm install on-finished
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
var onFinished = require('on-finished')
|
||||||
|
```
|
||||||
|
|
||||||
|
### onFinished(res, listener)
|
||||||
|
|
||||||
|
Attach a listener to listen for the response to finish. The listener will
|
||||||
|
be invoked only once when the response finished. If the response finished
|
||||||
|
to an error, the first argument will contain the error. If the response
|
||||||
|
has already finished, the listener will be invoked.
|
||||||
|
|
||||||
|
Listening to the end of a response would be used to close things associated
|
||||||
|
with the response, like open files.
|
||||||
|
|
||||||
|
Listener is invoked as `listener(err, res)`.
|
||||||
|
|
||||||
|
<!-- eslint-disable handle-callback-err -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
onFinished(res, function (err, res) {
|
||||||
|
// clean up open fds, etc.
|
||||||
|
// err contains the error if request error'd
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### onFinished(req, listener)
|
||||||
|
|
||||||
|
Attach a listener to listen for the request to finish. The listener will
|
||||||
|
be invoked only once when the request finished. If the request finished
|
||||||
|
to an error, the first argument will contain the error. If the request
|
||||||
|
has already finished, the listener will be invoked.
|
||||||
|
|
||||||
|
Listening to the end of a request would be used to know when to continue
|
||||||
|
after reading the data.
|
||||||
|
|
||||||
|
Listener is invoked as `listener(err, req)`.
|
||||||
|
|
||||||
|
<!-- eslint-disable handle-callback-err -->
|
||||||
|
|
||||||
|
```js
|
||||||
|
var data = ''
|
||||||
|
|
||||||
|
req.setEncoding('utf8')
|
||||||
|
req.on('data', function (str) {
|
||||||
|
data += str
|
||||||
|
})
|
||||||
|
|
||||||
|
onFinished(req, function (err, req) {
|
||||||
|
// data is read unless there is err
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### onFinished.isFinished(res)
|
||||||
|
|
||||||
|
Determine if `res` is already finished. This would be useful to check and
|
||||||
|
not even start certain operations if the response has already finished.
|
||||||
|
|
||||||
|
### onFinished.isFinished(req)
|
||||||
|
|
||||||
|
Determine if `req` is already finished. This would be useful to check and
|
||||||
|
not even start certain operations if the request has already finished.
|
||||||
|
|
||||||
|
## Special Node.js requests
|
||||||
|
|
||||||
|
### HTTP CONNECT method
|
||||||
|
|
||||||
|
The meaning of the `CONNECT` method from RFC 7231, section 4.3.6:
|
||||||
|
|
||||||
|
> The CONNECT method requests that the recipient establish a tunnel to
|
||||||
|
> the destination origin server identified by the request-target and,
|
||||||
|
> if successful, thereafter restrict its behavior to blind forwarding
|
||||||
|
> of packets, in both directions, until the tunnel is closed. Tunnels
|
||||||
|
> are commonly used to create an end-to-end virtual connection, through
|
||||||
|
> one or more proxies, which can then be secured using TLS (Transport
|
||||||
|
> Layer Security, [RFC5246]).
|
||||||
|
|
||||||
|
In Node.js, these request objects come from the `'connect'` event on
|
||||||
|
the HTTP server.
|
||||||
|
|
||||||
|
When this module is used on a HTTP `CONNECT` request, the request is
|
||||||
|
considered "finished" immediately, **due to limitations in the Node.js
|
||||||
|
interface**. This means if the `CONNECT` request contains a request entity,
|
||||||
|
the request will be considered "finished" even before it has been read.
|
||||||
|
|
||||||
|
There is no such thing as a response object to a `CONNECT` request in
|
||||||
|
Node.js, so there is no support for one.
|
||||||
|
|
||||||
|
### HTTP Upgrade request
|
||||||
|
|
||||||
|
The meaning of the `Upgrade` header from RFC 7230, section 6.1:
|
||||||
|
|
||||||
|
> The "Upgrade" header field is intended to provide a simple mechanism
|
||||||
|
> for transitioning from HTTP/1.1 to some other protocol on the same
|
||||||
|
> connection.
|
||||||
|
|
||||||
|
In Node.js, these request objects come from the `'upgrade'` event on
|
||||||
|
the HTTP server.
|
||||||
|
|
||||||
|
When this module is used on a HTTP request with an `Upgrade` header, the
|
||||||
|
request is considered "finished" immediately, **due to limitations in the
|
||||||
|
Node.js interface**. This means if the `Upgrade` request contains a request
|
||||||
|
entity, the request will be considered "finished" even before it has been
|
||||||
|
read.
|
||||||
|
|
||||||
|
There is no such thing as a response object to a `Upgrade` request in
|
||||||
|
Node.js, so there is no support for one.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
The following code ensures that file descriptors are always closed
|
||||||
|
once the response finishes.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var destroy = require('destroy')
|
||||||
|
var fs = require('fs')
|
||||||
|
var http = require('http')
|
||||||
|
var onFinished = require('on-finished')
|
||||||
|
|
||||||
|
http.createServer(function onRequest (req, res) {
|
||||||
|
var stream = fs.createReadStream('package.json')
|
||||||
|
stream.pipe(res)
|
||||||
|
onFinished(res, function () {
|
||||||
|
destroy(stream)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[ci-image]: https://badgen.net/github/checks/jshttp/on-finished/master?label=ci
|
||||||
|
[ci-url]: https://github.com/jshttp/on-finished/actions/workflows/ci.yml
|
||||||
|
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/on-finished/master
|
||||||
|
[coveralls-url]: https://coveralls.io/r/jshttp/on-finished?branch=master
|
||||||
|
[node-image]: https://badgen.net/npm/node/on-finished
|
||||||
|
[node-url]: https://nodejs.org/en/download
|
||||||
|
[npm-downloads-image]: https://badgen.net/npm/dm/on-finished
|
||||||
|
[npm-url]: https://npmjs.org/package/on-finished
|
||||||
|
[npm-version-image]: https://badgen.net/npm/v/on-finished
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
declare function isObject(x: unknown): x is object;
|
||||||
|
|
||||||
|
export = isObject;
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
1.0.5 / 2023-01-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
* perf: skip value escaping when unnecessary
|
||||||
|
|
||||||
|
1.0.4 / 2017-09-11
|
||||||
|
==================
|
||||||
|
|
||||||
|
* perf: skip parameter parsing when no parameters
|
||||||
|
|
||||||
|
1.0.3 / 2017-09-10
|
||||||
|
==================
|
||||||
|
|
||||||
|
* perf: remove argument reassignment
|
||||||
|
|
||||||
|
1.0.2 / 2016-05-09
|
||||||
|
==================
|
||||||
|
|
||||||
|
* perf: enable strict mode
|
||||||
|
|
||||||
|
1.0.1 / 2015-02-13
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Improve missing `Content-Type` header error message
|
||||||
|
|
||||||
|
1.0.0 / 2015-02-01
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Initial implementation, derived from `media-typer@0.3.0`
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
declare function setDunderProto<P extends null | object>(target: {}, proto: P): P;
|
||||||
|
|
||||||
|
declare const x: false | typeof setDunderProto;
|
||||||
|
|
||||||
|
export = x;
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
# side-channel <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
|
||||||
|
|
||||||
|
[![github actions][actions-image]][actions-url]
|
||||||
|
[![coverage][codecov-image]][codecov-url]
|
||||||
|
[![License][license-image]][license-url]
|
||||||
|
[![Downloads][downloads-image]][downloads-url]
|
||||||
|
|
||||||
|
[![npm badge][npm-badge-png]][package-url]
|
||||||
|
|
||||||
|
Store information about any JS value in a side channel. Uses WeakMap if available.
|
||||||
|
|
||||||
|
Warning: in an environment that lacks `WeakMap`, this implementation will leak memory until you `delete` the `key`.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save side-channel
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage/Examples
|
||||||
|
|
||||||
|
```js
|
||||||
|
const assert = require('assert');
|
||||||
|
const getSideChannel = require('side-channel');
|
||||||
|
|
||||||
|
const channel = getSideChannel();
|
||||||
|
|
||||||
|
const key = {};
|
||||||
|
assert.equal(channel.has(key), false);
|
||||||
|
assert.throws(() => channel.assert(key), TypeError);
|
||||||
|
|
||||||
|
channel.set(key, 42);
|
||||||
|
|
||||||
|
channel.assert(key); // does not throw
|
||||||
|
assert.equal(channel.has(key), true);
|
||||||
|
assert.equal(channel.get(key), 42);
|
||||||
|
|
||||||
|
channel.delete(key);
|
||||||
|
assert.equal(channel.has(key), false);
|
||||||
|
assert.throws(() => channel.assert(key), TypeError);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Clone the repo, `npm install`, and run `npm test`
|
||||||
|
|
||||||
|
[package-url]: https://npmjs.org/package/side-channel
|
||||||
|
[npm-version-svg]: https://versionbadg.es/ljharb/side-channel.svg
|
||||||
|
[deps-svg]: https://david-dm.org/ljharb/side-channel.svg
|
||||||
|
[deps-url]: https://david-dm.org/ljharb/side-channel
|
||||||
|
[dev-deps-svg]: https://david-dm.org/ljharb/side-channel/dev-status.svg
|
||||||
|
[dev-deps-url]: https://david-dm.org/ljharb/side-channel#info=devDependencies
|
||||||
|
[npm-badge-png]: https://nodei.co/npm/side-channel.png?downloads=true&stars=true
|
||||||
|
[license-image]: https://img.shields.io/npm/l/side-channel.svg
|
||||||
|
[license-url]: LICENSE
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/side-channel.svg
|
||||||
|
[downloads-url]: https://npm-stat.com/charts.html?package=side-channel
|
||||||
|
[codecov-image]: https://codecov.io/gh/ljharb/side-channel/branch/main/graphs/badge.svg
|
||||||
|
[codecov-url]: https://app.codecov.io/gh/ljharb/side-channel/
|
||||||
|
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/side-channel
|
||||||
|
[actions-url]: https://github.com/ljharb/side-channel/actions
|
||||||
@ -0,0 +1,225 @@
|
|||||||
|
var debug = require('debug')('nodemon');
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var exists = fs.exists || path.exists;
|
||||||
|
var utils = require('../utils');
|
||||||
|
var rules = require('../rules');
|
||||||
|
var parse = require('../rules/parse');
|
||||||
|
var exec = require('./exec');
|
||||||
|
var defaults = require('./defaults');
|
||||||
|
|
||||||
|
module.exports = load;
|
||||||
|
module.exports.mutateExecOptions = mutateExecOptions;
|
||||||
|
|
||||||
|
var existsSync = fs.existsSync || path.existsSync;
|
||||||
|
|
||||||
|
function findAppScript() {
|
||||||
|
// nodemon has been run alone, so try to read the package file
|
||||||
|
// or try to read the index.js file
|
||||||
|
|
||||||
|
var pkg =
|
||||||
|
existsSync(path.join(process.cwd(), 'package.json')) &&
|
||||||
|
require(path.join(process.cwd(), 'package.json'));
|
||||||
|
if ((!pkg || pkg.main == undefined) && existsSync('./index.js')) {
|
||||||
|
return 'index.js';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the nodemon config, first reading the global root/nodemon.json, then
|
||||||
|
* the local nodemon.json to the exec and then overwriting using any user
|
||||||
|
* specified settings (i.e. from the cli)
|
||||||
|
*
|
||||||
|
* @param {Object} settings user defined settings
|
||||||
|
* @param {Object} options global options
|
||||||
|
* @param {Object} config the config object to be updated
|
||||||
|
* @param {Function} callback that receives complete config
|
||||||
|
*/
|
||||||
|
function load(settings, options, config, callback) {
|
||||||
|
config.loaded = [];
|
||||||
|
// first load the root nodemon.json
|
||||||
|
loadFile(options, config, utils.home, function (options) {
|
||||||
|
// then load the user's local configuration file
|
||||||
|
if (settings.configFile) {
|
||||||
|
options.configFile = path.resolve(settings.configFile);
|
||||||
|
}
|
||||||
|
loadFile(options, config, process.cwd(), function (options) {
|
||||||
|
// Then merge over with the user settings (parsed from the cli).
|
||||||
|
// Note that merge protects and favours existing values over new values,
|
||||||
|
// and thus command line arguments get priority
|
||||||
|
options = utils.merge(settings, options);
|
||||||
|
|
||||||
|
// legacy support
|
||||||
|
if (!Array.isArray(options.ignore)) {
|
||||||
|
options.ignore = [options.ignore];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.ignoreRoot) {
|
||||||
|
options.ignoreRoot = defaults.ignoreRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// blend the user ignore and the default ignore together
|
||||||
|
if (options.ignoreRoot && options.ignore) {
|
||||||
|
if (!Array.isArray(options.ignoreRoot)) {
|
||||||
|
options.ignoreRoot = [options.ignoreRoot];
|
||||||
|
}
|
||||||
|
options.ignore = options.ignoreRoot.concat(options.ignore);
|
||||||
|
} else {
|
||||||
|
options.ignore = defaults.ignore.concat(options.ignore);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add in any missing defaults
|
||||||
|
options = utils.merge(options, defaults);
|
||||||
|
|
||||||
|
if (!options.script && !options.exec) {
|
||||||
|
var found = findAppScript();
|
||||||
|
if (found) {
|
||||||
|
if (!options.args) {
|
||||||
|
options.args = [];
|
||||||
|
}
|
||||||
|
// if the script is found as a result of not being on the command
|
||||||
|
// line, then we move any of the pre double-dash args in execArgs
|
||||||
|
const n =
|
||||||
|
options.scriptPosition === null
|
||||||
|
? options.args.length
|
||||||
|
: options.scriptPosition;
|
||||||
|
|
||||||
|
options.execArgs = (options.execArgs || []).concat(
|
||||||
|
options.args.splice(0, n)
|
||||||
|
);
|
||||||
|
options.scriptPosition = null;
|
||||||
|
|
||||||
|
options.script = found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutateExecOptions(options);
|
||||||
|
|
||||||
|
if (options.quiet) {
|
||||||
|
utils.quiet();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.verbose) {
|
||||||
|
utils.debug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// simplify the ready callback to be called after the rules are normalised
|
||||||
|
// from strings to regexp through the rules lib. Note that this gets
|
||||||
|
// created *after* options is overwritten twice in the lines above.
|
||||||
|
var ready = function (options) {
|
||||||
|
normaliseRules(options, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
ready(options);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function normaliseRules(options, ready) {
|
||||||
|
// convert ignore and watch options to rules/regexp
|
||||||
|
rules.watch.add(options.watch);
|
||||||
|
rules.ignore.add(options.ignore);
|
||||||
|
|
||||||
|
// normalise the watch and ignore arrays
|
||||||
|
options.watch = options.watch === false ? false : rules.rules.watch;
|
||||||
|
options.ignore = rules.rules.ignore;
|
||||||
|
|
||||||
|
ready(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for a config in the current working directory, and a config in the
|
||||||
|
* user's home directory, merging the two together, giving priority to local
|
||||||
|
* config. This can then be overwritten later by command line arguments
|
||||||
|
*
|
||||||
|
* @param {Function} ready callback to pass loaded settings to
|
||||||
|
*/
|
||||||
|
function loadFile(options, config, dir, ready) {
|
||||||
|
if (!ready) {
|
||||||
|
ready = function () {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var callback = function (settings) {
|
||||||
|
// prefer the local nodemon.json and fill in missing items using
|
||||||
|
// the global options
|
||||||
|
ready(utils.merge(settings, options));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!dir) {
|
||||||
|
return callback({});
|
||||||
|
}
|
||||||
|
|
||||||
|
var filename = options.configFile || path.join(dir, 'nodemon.json');
|
||||||
|
|
||||||
|
if (config.loaded.indexOf(filename) !== -1) {
|
||||||
|
// don't bother re-parsing the same config file
|
||||||
|
return callback({});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.readFile(filename, 'utf8', function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
if (!options.configFile && dir !== utils.home) {
|
||||||
|
// if no specified local config file and local nodemon.json
|
||||||
|
// doesn't exist, try the package.json
|
||||||
|
return loadPackageJSON(config, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return callback({});
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
settings = JSON.parse(data.toString('utf8').replace(/^\uFEFF/, ''));
|
||||||
|
if (!filename.endsWith('package.json') || settings.nodemonConfig) {
|
||||||
|
config.loaded.push(filename);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
utils.log.fail('Failed to parse config ' + filename);
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// options values will overwrite settings
|
||||||
|
callback(settings);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPackageJSON(config, ready) {
|
||||||
|
if (!ready) {
|
||||||
|
ready = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const dir = process.cwd();
|
||||||
|
const filename = path.join(dir, 'package.json');
|
||||||
|
const packageLoadOptions = { configFile: filename };
|
||||||
|
return loadFile(packageLoadOptions, config, dir, (settings) => {
|
||||||
|
ready(settings.nodemonConfig || {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutateExecOptions(options) {
|
||||||
|
// work out the execOptions based on the final config we have
|
||||||
|
options.execOptions = exec(
|
||||||
|
{
|
||||||
|
script: options.script,
|
||||||
|
exec: options.exec,
|
||||||
|
args: options.args,
|
||||||
|
scriptPosition: options.scriptPosition,
|
||||||
|
nodeArgs: options.nodeArgs,
|
||||||
|
execArgs: options.execArgs,
|
||||||
|
ext: options.ext,
|
||||||
|
env: options.env,
|
||||||
|
},
|
||||||
|
options.execMap
|
||||||
|
);
|
||||||
|
|
||||||
|
// clean up values that we don't need at the top level
|
||||||
|
delete options.scriptPosition;
|
||||||
|
delete options.script;
|
||||||
|
delete options.args;
|
||||||
|
delete options.ext;
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
var ignoreRoot = require('ignore-by-default').directories();
|
||||||
|
|
||||||
|
// default options for config.options
|
||||||
|
const defaults = {
|
||||||
|
restartable: 'rs',
|
||||||
|
colours: true,
|
||||||
|
execMap: {
|
||||||
|
py: 'python',
|
||||||
|
rb: 'ruby',
|
||||||
|
ts: 'ts-node',
|
||||||
|
// more can be added here such as ls: lsc - but please ensure it's cross
|
||||||
|
// compatible with linux, mac and windows, or make the default.js
|
||||||
|
// dynamically append the `.cmd` for node based utilities
|
||||||
|
},
|
||||||
|
ignoreRoot: ignoreRoot.map((_) => `**/${_}/**`),
|
||||||
|
watch: ['*.*'],
|
||||||
|
stdin: true,
|
||||||
|
runOnChangeOnly: false,
|
||||||
|
verbose: false,
|
||||||
|
signal: 'SIGUSR2',
|
||||||
|
// 'stdout' refers to the default behaviour of a required nodemon's child,
|
||||||
|
// but also includes stderr. If this is false, data is still dispatched via
|
||||||
|
// nodemon.on('stdout/stderr')
|
||||||
|
stdout: true,
|
||||||
|
watchOptions: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const nodeOptions = process.env.NODE_OPTIONS || ''; // ?
|
||||||
|
|
||||||
|
if (/--(loader|import)\b/.test(nodeOptions)) {
|
||||||
|
delete defaults.execMap.ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = defaults;
|
||||||
@ -0,0 +1,421 @@
|
|||||||
|
3.0.1 / 2025-03-26
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.54.0
|
||||||
|
|
||||||
|
3.0.0 / 2024-08-31
|
||||||
|
===================
|
||||||
|
|
||||||
|
* Drop support for node <18
|
||||||
|
* deps: mime-db@1.53.0
|
||||||
|
* resolve extension conflicts with mime-score (#119)
|
||||||
|
* asc -> application/pgp-signature is now application/pgp-keys
|
||||||
|
* mpp -> application/vnd.ms-project is now application/dash-patch+xml
|
||||||
|
* ac -> application/vnd.nokia.n-gage.ac+xml is now application/pkix-attr-cert
|
||||||
|
* bdoc -> application/x-bdoc is now application/bdoc
|
||||||
|
* wmz -> application/x-msmetafile is now application/x-ms-wmz
|
||||||
|
* xsl -> application/xslt+xml is now application/xml
|
||||||
|
* wav -> audio/wave is now audio/wav
|
||||||
|
* rtf -> text/rtf is now application/rtf
|
||||||
|
* xml -> text/xml is now application/xml
|
||||||
|
* mp4 -> video/mp4 is now application/mp4
|
||||||
|
* mpg4 -> video/mp4 is now application/mp4
|
||||||
|
|
||||||
|
|
||||||
|
2.1.35 / 2022-03-12
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.52.0
|
||||||
|
- Add extensions from IANA for more `image/*` types
|
||||||
|
- Add extension `.asc` to `application/pgp-keys`
|
||||||
|
- Add extensions to various XML types
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.34 / 2021-11-08
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.51.0
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.33 / 2021-10-01
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.50.0
|
||||||
|
- Add deprecated iWorks mime types and extensions
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.32 / 2021-07-27
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.49.0
|
||||||
|
- Add extension `.trig` to `application/trig`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.31 / 2021-06-01
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.48.0
|
||||||
|
- Add extension `.mvt` to `application/vnd.mapbox-vector-tile`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.30 / 2021-04-02
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.47.0
|
||||||
|
- Add extension `.amr` to `audio/amr`
|
||||||
|
- Remove ambigious extensions from IANA for `application/*+xml` types
|
||||||
|
- Update primary extension to `.es` for `application/ecmascript`
|
||||||
|
|
||||||
|
2.1.29 / 2021-02-17
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.46.0
|
||||||
|
- Add extension `.amr` to `audio/amr`
|
||||||
|
- Add extension `.m4s` to `video/iso.segment`
|
||||||
|
- Add extension `.opus` to `audio/ogg`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.28 / 2021-01-01
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.45.0
|
||||||
|
- Add `application/ubjson` with extension `.ubj`
|
||||||
|
- Add `image/avif` with extension `.avif`
|
||||||
|
- Add `image/ktx2` with extension `.ktx2`
|
||||||
|
- Add extension `.dbf` to `application/vnd.dbf`
|
||||||
|
- Add extension `.rar` to `application/vnd.rar`
|
||||||
|
- Add extension `.td` to `application/urc-targetdesc+xml`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
- Fix extension of `application/vnd.apple.keynote` to be `.key`
|
||||||
|
|
||||||
|
2.1.27 / 2020-04-23
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.44.0
|
||||||
|
- Add charsets from IANA
|
||||||
|
- Add extension `.cjs` to `application/node`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.26 / 2020-01-05
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.43.0
|
||||||
|
- Add `application/x-keepass2` with extension `.kdbx`
|
||||||
|
- Add extension `.mxmf` to `audio/mobile-xmf`
|
||||||
|
- Add extensions from IANA for `application/*+xml` types
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.25 / 2019-11-12
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.42.0
|
||||||
|
- Add new upstream MIME types
|
||||||
|
- Add `application/toml` with extension `.toml`
|
||||||
|
- Add `image/vnd.ms-dds` with extension `.dds`
|
||||||
|
|
||||||
|
2.1.24 / 2019-04-20
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@1.40.0
|
||||||
|
- Add extensions from IANA for `model/*` types
|
||||||
|
- Add `text/mdx` with extension `.mdx`
|
||||||
|
|
||||||
|
2.1.23 / 2019-04-17
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.39.0
|
||||||
|
- Add extensions `.siv` and `.sieve` to `application/sieve`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.22 / 2019-02-14
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.38.0
|
||||||
|
- Add extension `.nq` to `application/n-quads`
|
||||||
|
- Add extension `.nt` to `application/n-triples`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.21 / 2018-10-19
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.37.0
|
||||||
|
- Add extensions to HEIC image types
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.20 / 2018-08-26
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.36.0
|
||||||
|
- Add Apple file extensions from IANA
|
||||||
|
- Add extensions from IANA for `image/*` types
|
||||||
|
- Add new upstream MIME types
|
||||||
|
|
||||||
|
2.1.19 / 2018-07-17
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.35.0
|
||||||
|
- Add extension `.csl` to `application/vnd.citationstyles.style+xml`
|
||||||
|
- Add extension `.es` to `application/ecmascript`
|
||||||
|
- Add extension `.owl` to `application/rdf+xml`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
- Add UTF-8 as default charset for `text/turtle`
|
||||||
|
|
||||||
|
2.1.18 / 2018-02-16
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.33.0
|
||||||
|
- Add `application/raml+yaml` with extension `.raml`
|
||||||
|
- Add `application/wasm` with extension `.wasm`
|
||||||
|
- Add `text/shex` with extension `.shex`
|
||||||
|
- Add extensions for JPEG-2000 images
|
||||||
|
- Add extensions from IANA for `message/*` types
|
||||||
|
- Add new upstream MIME types
|
||||||
|
- Update font MIME types
|
||||||
|
- Update `text/hjson` to registered `application/hjson`
|
||||||
|
|
||||||
|
2.1.17 / 2017-09-01
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.30.0
|
||||||
|
- Add `application/vnd.ms-outlook`
|
||||||
|
- Add `application/x-arj`
|
||||||
|
- Add extension `.mjs` to `application/javascript`
|
||||||
|
- Add glTF types and extensions
|
||||||
|
- Add new upstream MIME types
|
||||||
|
- Add `text/x-org`
|
||||||
|
- Add VirtualBox MIME types
|
||||||
|
- Fix `source` records for `video/*` types that are IANA
|
||||||
|
- Update `font/opentype` to registered `font/otf`
|
||||||
|
|
||||||
|
2.1.16 / 2017-07-24
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.29.0
|
||||||
|
- Add `application/fido.trusted-apps+json`
|
||||||
|
- Add extension `.wadl` to `application/vnd.sun.wadl+xml`
|
||||||
|
- Add extension `.gz` to `application/gzip`
|
||||||
|
- Add new upstream MIME types
|
||||||
|
- Update extensions `.md` and `.markdown` to be `text/markdown`
|
||||||
|
|
||||||
|
2.1.15 / 2017-03-23
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.27.0
|
||||||
|
- Add new mime types
|
||||||
|
- Add `image/apng`
|
||||||
|
|
||||||
|
2.1.14 / 2017-01-14
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.26.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.13 / 2016-11-18
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.25.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.12 / 2016-09-18
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.24.0
|
||||||
|
- Add new mime types
|
||||||
|
- Add `audio/mp3`
|
||||||
|
|
||||||
|
2.1.11 / 2016-05-01
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.23.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.10 / 2016-02-15
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.22.0
|
||||||
|
- Add new mime types
|
||||||
|
- Fix extension of `application/dash+xml`
|
||||||
|
- Update primary extension for `audio/mp4`
|
||||||
|
|
||||||
|
2.1.9 / 2016-01-06
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.21.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.8 / 2015-11-30
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.20.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.7 / 2015-09-20
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.19.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.6 / 2015-09-03
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.18.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.5 / 2015-08-20
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.17.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.4 / 2015-07-30
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.16.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.3 / 2015-07-13
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.15.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.2 / 2015-06-25
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.14.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.1.1 / 2015-06-08
|
||||||
|
==================
|
||||||
|
|
||||||
|
* perf: fix deopt during mapping
|
||||||
|
|
||||||
|
2.1.0 / 2015-06-07
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix incorrectly treating extension-less file name as extension
|
||||||
|
- i.e. `'path/to/json'` will no longer return `application/json`
|
||||||
|
* Fix `.charset(type)` to accept parameters
|
||||||
|
* Fix `.charset(type)` to match case-insensitive
|
||||||
|
* Improve generation of extension to MIME mapping
|
||||||
|
* Refactor internals for readability and no argument reassignment
|
||||||
|
* Prefer `application/*` MIME types from the same source
|
||||||
|
* Prefer any type over `application/octet-stream`
|
||||||
|
* deps: mime-db@~1.13.0
|
||||||
|
- Add nginx as a source
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.14 / 2015-06-06
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.12.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.13 / 2015-05-31
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.11.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.12 / 2015-05-19
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.10.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.11 / 2015-05-05
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.9.1
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.10 / 2015-03-13
|
||||||
|
===================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.8.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.9 / 2015-02-09
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.7.0
|
||||||
|
- Add new mime types
|
||||||
|
- Community extensions ownership transferred from `node-mime`
|
||||||
|
|
||||||
|
2.0.8 / 2015-01-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.6.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.7 / 2014-12-30
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.5.0
|
||||||
|
- Add new mime types
|
||||||
|
- Fix various invalid MIME type entries
|
||||||
|
|
||||||
|
2.0.6 / 2014-12-30
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.4.0
|
||||||
|
- Add new mime types
|
||||||
|
- Fix various invalid MIME type entries
|
||||||
|
- Remove example template MIME types
|
||||||
|
|
||||||
|
2.0.5 / 2014-12-29
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.3.1
|
||||||
|
- Fix missing extensions
|
||||||
|
|
||||||
|
2.0.4 / 2014-12-10
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.3.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.3 / 2014-11-09
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.2.0
|
||||||
|
- Add new mime types
|
||||||
|
|
||||||
|
2.0.2 / 2014-09-28
|
||||||
|
==================
|
||||||
|
|
||||||
|
* deps: mime-db@~1.1.0
|
||||||
|
- Add new mime types
|
||||||
|
- Update charsets
|
||||||
|
|
||||||
|
2.0.1 / 2014-09-07
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Support Node.js 0.6
|
||||||
|
|
||||||
|
2.0.0 / 2014-09-02
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Use `mime-db`
|
||||||
|
* Remove `.define()`
|
||||||
|
|
||||||
|
1.0.2 / 2014-08-04
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Set charset=utf-8 for `text/javascript`
|
||||||
|
|
||||||
|
1.0.1 / 2014-06-24
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Add `text/jsx` type
|
||||||
|
|
||||||
|
1.0.0 / 2014-05-12
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Return `false` for unknown types
|
||||||
|
* Set charset=utf-8 for `application/json`
|
||||||
|
|
||||||
|
0.1.0 / 2014-05-02
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Initial release
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"name": "path-to-regexp",
|
||||||
|
"version": "8.2.0",
|
||||||
|
"description": "Express style path to RegExp utility",
|
||||||
|
"keywords": [
|
||||||
|
"express",
|
||||||
|
"regexp",
|
||||||
|
"route",
|
||||||
|
"routing"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/pillarjs/path-to-regexp.git"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"typings": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"bench": "vitest bench",
|
||||||
|
"build": "ts-scripts build",
|
||||||
|
"format": "ts-scripts format",
|
||||||
|
"lint": "ts-scripts lint",
|
||||||
|
"prepare": "ts-scripts install && npm run build",
|
||||||
|
"size": "size-limit",
|
||||||
|
"specs": "ts-scripts specs",
|
||||||
|
"test": "ts-scripts test && npm run size"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@borderless/ts-scripts": "^0.15.0",
|
||||||
|
"@size-limit/preset-small-lib": "^11.1.2",
|
||||||
|
"@types/node": "^22.7.2",
|
||||||
|
"@types/semver": "^7.3.1",
|
||||||
|
"@vitest/coverage-v8": "^2.1.1",
|
||||||
|
"recheck": "^4.4.5",
|
||||||
|
"size-limit": "^11.1.2",
|
||||||
|
"typescript": "^5.5.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"size-limit": [
|
||||||
|
{
|
||||||
|
"path": "dist/index.js",
|
||||||
|
"limit": "2.2 kB"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ts-scripts": {
|
||||||
|
"dist": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"project": [
|
||||||
|
"tsconfig.build.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,293 @@
|
|||||||
|
/*!
|
||||||
|
* finalhandler
|
||||||
|
* Copyright(c) 2014-2022 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var debug = require('debug')('finalhandler')
|
||||||
|
var encodeUrl = require('encodeurl')
|
||||||
|
var escapeHtml = require('escape-html')
|
||||||
|
var onFinished = require('on-finished')
|
||||||
|
var parseUrl = require('parseurl')
|
||||||
|
var statuses = require('statuses')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module variables.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var isFinished = onFinished.isFinished
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a minimal HTML document.
|
||||||
|
*
|
||||||
|
* @param {string} message
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function createHtmlDocument (message) {
|
||||||
|
var body = escapeHtml(message)
|
||||||
|
.replaceAll('\n', '<br>')
|
||||||
|
.replaceAll(' ', ' ')
|
||||||
|
|
||||||
|
return '<!DOCTYPE html>\n' +
|
||||||
|
'<html lang="en">\n' +
|
||||||
|
'<head>\n' +
|
||||||
|
'<meta charset="utf-8">\n' +
|
||||||
|
'<title>Error</title>\n' +
|
||||||
|
'</head>\n' +
|
||||||
|
'<body>\n' +
|
||||||
|
'<pre>' + body + '</pre>\n' +
|
||||||
|
'</body>\n' +
|
||||||
|
'</html>\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = finalhandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a function to handle the final response.
|
||||||
|
*
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {Object} [options]
|
||||||
|
* @return {Function}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function finalhandler (req, res, options) {
|
||||||
|
var opts = options || {}
|
||||||
|
|
||||||
|
// get environment
|
||||||
|
var env = opts.env || process.env.NODE_ENV || 'development'
|
||||||
|
|
||||||
|
// get error callback
|
||||||
|
var onerror = opts.onerror
|
||||||
|
|
||||||
|
return function (err) {
|
||||||
|
var headers
|
||||||
|
var msg
|
||||||
|
var status
|
||||||
|
|
||||||
|
// ignore 404 on in-flight response
|
||||||
|
if (!err && res.headersSent) {
|
||||||
|
debug('cannot 404 after headers sent')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// unhandled error
|
||||||
|
if (err) {
|
||||||
|
// respect status code from error
|
||||||
|
status = getErrorStatusCode(err)
|
||||||
|
|
||||||
|
if (status === undefined) {
|
||||||
|
// fallback to status code on response
|
||||||
|
status = getResponseStatusCode(res)
|
||||||
|
} else {
|
||||||
|
// respect headers from error
|
||||||
|
headers = getErrorHeaders(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get error message
|
||||||
|
msg = getErrorMessage(err, status, env)
|
||||||
|
} else {
|
||||||
|
// not found
|
||||||
|
status = 404
|
||||||
|
msg = 'Cannot ' + req.method + ' ' + encodeUrl(getResourceName(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('default %s', status)
|
||||||
|
|
||||||
|
// schedule onerror callback
|
||||||
|
if (err && onerror) {
|
||||||
|
setImmediate(onerror, err, req, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cannot actually respond
|
||||||
|
if (res.headersSent) {
|
||||||
|
debug('cannot %d after headers sent', status)
|
||||||
|
if (req.socket) {
|
||||||
|
req.socket.destroy()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// send response
|
||||||
|
send(req, res, status, headers, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get headers from Error object.
|
||||||
|
*
|
||||||
|
* @param {Error} err
|
||||||
|
* @return {object}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getErrorHeaders (err) {
|
||||||
|
if (!err.headers || typeof err.headers !== 'object') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...err.headers }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get message from Error object, fallback to status message.
|
||||||
|
*
|
||||||
|
* @param {Error} err
|
||||||
|
* @param {number} status
|
||||||
|
* @param {string} env
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getErrorMessage (err, status, env) {
|
||||||
|
var msg
|
||||||
|
|
||||||
|
if (env !== 'production') {
|
||||||
|
// use err.stack, which typically includes err.message
|
||||||
|
msg = err.stack
|
||||||
|
|
||||||
|
// fallback to err.toString() when possible
|
||||||
|
if (!msg && typeof err.toString === 'function') {
|
||||||
|
msg = err.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg || statuses.message[status]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get status code from Error object.
|
||||||
|
*
|
||||||
|
* @param {Error} err
|
||||||
|
* @return {number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getErrorStatusCode (err) {
|
||||||
|
// check err.status
|
||||||
|
if (typeof err.status === 'number' && err.status >= 400 && err.status < 600) {
|
||||||
|
return err.status
|
||||||
|
}
|
||||||
|
|
||||||
|
// check err.statusCode
|
||||||
|
if (typeof err.statusCode === 'number' && err.statusCode >= 400 && err.statusCode < 600) {
|
||||||
|
return err.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource name for the request.
|
||||||
|
*
|
||||||
|
* This is typically just the original pathname of the request
|
||||||
|
* but will fallback to "resource" is that cannot be determined.
|
||||||
|
*
|
||||||
|
* @param {IncomingMessage} req
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getResourceName (req) {
|
||||||
|
try {
|
||||||
|
return parseUrl.original(req).pathname
|
||||||
|
} catch (e) {
|
||||||
|
return 'resource'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get status code from response.
|
||||||
|
*
|
||||||
|
* @param {OutgoingMessage} res
|
||||||
|
* @return {number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getResponseStatusCode (res) {
|
||||||
|
var status = res.statusCode
|
||||||
|
|
||||||
|
// default status code to 500 if outside valid range
|
||||||
|
if (typeof status !== 'number' || status < 400 || status > 599) {
|
||||||
|
status = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send response.
|
||||||
|
*
|
||||||
|
* @param {IncomingMessage} req
|
||||||
|
* @param {OutgoingMessage} res
|
||||||
|
* @param {number} status
|
||||||
|
* @param {object} headers
|
||||||
|
* @param {string} message
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function send (req, res, status, headers, message) {
|
||||||
|
function write () {
|
||||||
|
// response body
|
||||||
|
var body = createHtmlDocument(message)
|
||||||
|
|
||||||
|
// response status
|
||||||
|
res.statusCode = status
|
||||||
|
|
||||||
|
if (req.httpVersionMajor < 2) {
|
||||||
|
res.statusMessage = statuses.message[status]
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove any content headers
|
||||||
|
res.removeHeader('Content-Encoding')
|
||||||
|
res.removeHeader('Content-Language')
|
||||||
|
res.removeHeader('Content-Range')
|
||||||
|
|
||||||
|
// response headers
|
||||||
|
for (const [key, value] of Object.entries(headers ?? {})) {
|
||||||
|
res.setHeader(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// security headers
|
||||||
|
res.setHeader('Content-Security-Policy', "default-src 'none'")
|
||||||
|
res.setHeader('X-Content-Type-Options', 'nosniff')
|
||||||
|
|
||||||
|
// standard headers
|
||||||
|
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
|
||||||
|
|
||||||
|
if (req.method === 'HEAD') {
|
||||||
|
res.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end(body, 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFinished(req)) {
|
||||||
|
write()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpipe everything from the request
|
||||||
|
req.unpipe()
|
||||||
|
|
||||||
|
// flush the request
|
||||||
|
onFinished(req, write)
|
||||||
|
req.resume()
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2018 Douglas Christopher Wilson
|
||||||
|
|
||||||
|
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.
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,249 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Range = require('../classes/range.js')
|
||||||
|
const Comparator = require('../classes/comparator.js')
|
||||||
|
const { ANY } = Comparator
|
||||||
|
const satisfies = require('../functions/satisfies.js')
|
||||||
|
const compare = require('../functions/compare.js')
|
||||||
|
|
||||||
|
// Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff:
|
||||||
|
// - Every simple range `r1, r2, ...` is a null set, OR
|
||||||
|
// - Every simple range `r1, r2, ...` which is not a null set is a subset of
|
||||||
|
// some `R1, R2, ...`
|
||||||
|
//
|
||||||
|
// Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:
|
||||||
|
// - If c is only the ANY comparator
|
||||||
|
// - If C is only the ANY comparator, return true
|
||||||
|
// - Else if in prerelease mode, return false
|
||||||
|
// - else replace c with `[>=0.0.0]`
|
||||||
|
// - If C is only the ANY comparator
|
||||||
|
// - if in prerelease mode, return true
|
||||||
|
// - else replace C with `[>=0.0.0]`
|
||||||
|
// - Let EQ be the set of = comparators in c
|
||||||
|
// - If EQ is more than one, return true (null set)
|
||||||
|
// - Let GT be the highest > or >= comparator in c
|
||||||
|
// - Let LT be the lowest < or <= comparator in c
|
||||||
|
// - If GT and LT, and GT.semver > LT.semver, return true (null set)
|
||||||
|
// - If any C is a = range, and GT or LT are set, return false
|
||||||
|
// - If EQ
|
||||||
|
// - If GT, and EQ does not satisfy GT, return true (null set)
|
||||||
|
// - If LT, and EQ does not satisfy LT, return true (null set)
|
||||||
|
// - If EQ satisfies every C, return true
|
||||||
|
// - Else return false
|
||||||
|
// - If GT
|
||||||
|
// - If GT.semver is lower than any > or >= comp in C, return false
|
||||||
|
// - If GT is >=, and GT.semver does not satisfy every C, return false
|
||||||
|
// - If GT.semver has a prerelease, and not in prerelease mode
|
||||||
|
// - If no C has a prerelease and the GT.semver tuple, return false
|
||||||
|
// - If LT
|
||||||
|
// - If LT.semver is greater than any < or <= comp in C, return false
|
||||||
|
// - If LT is <=, and LT.semver does not satisfy every C, return false
|
||||||
|
// - If GT.semver has a prerelease, and not in prerelease mode
|
||||||
|
// - If no C has a prerelease and the LT.semver tuple, return false
|
||||||
|
// - Else return true
|
||||||
|
|
||||||
|
const subset = (sub, dom, options = {}) => {
|
||||||
|
if (sub === dom) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub = new Range(sub, options)
|
||||||
|
dom = new Range(dom, options)
|
||||||
|
let sawNonNull = false
|
||||||
|
|
||||||
|
OUTER: for (const simpleSub of sub.set) {
|
||||||
|
for (const simpleDom of dom.set) {
|
||||||
|
const isSub = simpleSubset(simpleSub, simpleDom, options)
|
||||||
|
sawNonNull = sawNonNull || isSub !== null
|
||||||
|
if (isSub) {
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the null set is a subset of everything, but null simple ranges in
|
||||||
|
// a complex range should be ignored. so if we saw a non-null range,
|
||||||
|
// then we know this isn't a subset, but if EVERY simple range was null,
|
||||||
|
// then it is a subset.
|
||||||
|
if (sawNonNull) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const minimumVersionWithPreRelease = [new Comparator('>=0.0.0-0')]
|
||||||
|
const minimumVersion = [new Comparator('>=0.0.0')]
|
||||||
|
|
||||||
|
const simpleSubset = (sub, dom, options) => {
|
||||||
|
if (sub === dom) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub.length === 1 && sub[0].semver === ANY) {
|
||||||
|
if (dom.length === 1 && dom[0].semver === ANY) {
|
||||||
|
return true
|
||||||
|
} else if (options.includePrerelease) {
|
||||||
|
sub = minimumVersionWithPreRelease
|
||||||
|
} else {
|
||||||
|
sub = minimumVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dom.length === 1 && dom[0].semver === ANY) {
|
||||||
|
if (options.includePrerelease) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
dom = minimumVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const eqSet = new Set()
|
||||||
|
let gt, lt
|
||||||
|
for (const c of sub) {
|
||||||
|
if (c.operator === '>' || c.operator === '>=') {
|
||||||
|
gt = higherGT(gt, c, options)
|
||||||
|
} else if (c.operator === '<' || c.operator === '<=') {
|
||||||
|
lt = lowerLT(lt, c, options)
|
||||||
|
} else {
|
||||||
|
eqSet.add(c.semver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eqSet.size > 1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let gtltComp
|
||||||
|
if (gt && lt) {
|
||||||
|
gtltComp = compare(gt.semver, lt.semver, options)
|
||||||
|
if (gtltComp > 0) {
|
||||||
|
return null
|
||||||
|
} else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// will iterate one or zero times
|
||||||
|
for (const eq of eqSet) {
|
||||||
|
if (gt && !satisfies(eq, String(gt), options)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lt && !satisfies(eq, String(lt), options)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const c of dom) {
|
||||||
|
if (!satisfies(eq, String(c), options)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let higher, lower
|
||||||
|
let hasDomLT, hasDomGT
|
||||||
|
// if the subset has a prerelease, we need a comparator in the superset
|
||||||
|
// with the same tuple and a prerelease, or it's not a subset
|
||||||
|
let needDomLTPre = lt &&
|
||||||
|
!options.includePrerelease &&
|
||||||
|
lt.semver.prerelease.length ? lt.semver : false
|
||||||
|
let needDomGTPre = gt &&
|
||||||
|
!options.includePrerelease &&
|
||||||
|
gt.semver.prerelease.length ? gt.semver : false
|
||||||
|
// exception: <1.2.3-0 is the same as <1.2.3
|
||||||
|
if (needDomLTPre && needDomLTPre.prerelease.length === 1 &&
|
||||||
|
lt.operator === '<' && needDomLTPre.prerelease[0] === 0) {
|
||||||
|
needDomLTPre = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const c of dom) {
|
||||||
|
hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>='
|
||||||
|
hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<='
|
||||||
|
if (gt) {
|
||||||
|
if (needDomGTPre) {
|
||||||
|
if (c.semver.prerelease && c.semver.prerelease.length &&
|
||||||
|
c.semver.major === needDomGTPre.major &&
|
||||||
|
c.semver.minor === needDomGTPre.minor &&
|
||||||
|
c.semver.patch === needDomGTPre.patch) {
|
||||||
|
needDomGTPre = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.operator === '>' || c.operator === '>=') {
|
||||||
|
higher = higherGT(gt, c, options)
|
||||||
|
if (higher === c && higher !== gt) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if (gt.operator === '>=' && !satisfies(gt.semver, String(c), options)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lt) {
|
||||||
|
if (needDomLTPre) {
|
||||||
|
if (c.semver.prerelease && c.semver.prerelease.length &&
|
||||||
|
c.semver.major === needDomLTPre.major &&
|
||||||
|
c.semver.minor === needDomLTPre.minor &&
|
||||||
|
c.semver.patch === needDomLTPre.patch) {
|
||||||
|
needDomLTPre = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.operator === '<' || c.operator === '<=') {
|
||||||
|
lower = lowerLT(lt, c, options)
|
||||||
|
if (lower === c && lower !== lt) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if (lt.operator === '<=' && !satisfies(lt.semver, String(c), options)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!c.operator && (lt || gt) && gtltComp !== 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there was a < or >, and nothing in the dom, then must be false
|
||||||
|
// UNLESS it was limited by another range in the other direction.
|
||||||
|
// Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0
|
||||||
|
if (gt && hasDomLT && !lt && gtltComp !== 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lt && hasDomGT && !gt && gtltComp !== 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// we needed a prerelease range in a specific tuple, but didn't get one
|
||||||
|
// then this isn't a subset. eg >=1.2.3-pre is not a subset of >=1.0.0,
|
||||||
|
// because it includes prereleases in the 1.2.3 tuple
|
||||||
|
if (needDomGTPre || needDomLTPre) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// >=1.2.3 is lower than >1.2.3
|
||||||
|
const higherGT = (a, b, options) => {
|
||||||
|
if (!a) {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
const comp = compare(a.semver, b.semver, options)
|
||||||
|
return comp > 0 ? a
|
||||||
|
: comp < 0 ? b
|
||||||
|
: b.operator === '>' && a.operator === '>=' ? b
|
||||||
|
: a
|
||||||
|
}
|
||||||
|
|
||||||
|
// <=1.2.3 is higher than <1.2.3
|
||||||
|
const lowerLT = (a, b, options) => {
|
||||||
|
if (!a) {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
const comp = compare(a.semver, b.semver, options)
|
||||||
|
return comp < 0 ? a
|
||||||
|
: comp > 0 ? b
|
||||||
|
: b.operator === '<' && a.operator === '<=' ? b
|
||||||
|
: a
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = subset
|
||||||
@ -0,0 +1,406 @@
|
|||||||
|
/* eslint-disable node/no-deprecated-api */
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
var test = require('tape')
|
||||||
|
|
||||||
|
var buffer = require('buffer')
|
||||||
|
|
||||||
|
var index = require('./')
|
||||||
|
var safer = require('./safer')
|
||||||
|
var dangerous = require('./dangerous')
|
||||||
|
|
||||||
|
/* Inheritance tests */
|
||||||
|
|
||||||
|
test('Default is Safer', function (t) {
|
||||||
|
t.equal(index, safer)
|
||||||
|
t.notEqual(safer, dangerous)
|
||||||
|
t.notEqual(index, dangerous)
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Is not a function', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(typeof impl, 'object')
|
||||||
|
t.equal(typeof impl.Buffer, 'object')
|
||||||
|
});
|
||||||
|
[buffer].forEach(function (impl) {
|
||||||
|
t.equal(typeof impl, 'object')
|
||||||
|
t.equal(typeof impl.Buffer, 'function')
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Constructor throws', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.throws(function () { impl.Buffer() })
|
||||||
|
t.throws(function () { impl.Buffer(0) })
|
||||||
|
t.throws(function () { impl.Buffer('a') })
|
||||||
|
t.throws(function () { impl.Buffer('a', 'utf-8') })
|
||||||
|
t.throws(function () { return new impl.Buffer() })
|
||||||
|
t.throws(function () { return new impl.Buffer(0) })
|
||||||
|
t.throws(function () { return new impl.Buffer('a') })
|
||||||
|
t.throws(function () { return new impl.Buffer('a', 'utf-8') })
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Safe methods exist', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(typeof impl.Buffer.alloc, 'function', 'alloc')
|
||||||
|
t.equal(typeof impl.Buffer.from, 'function', 'from')
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Unsafe methods exist only in Dangerous', function (t) {
|
||||||
|
[index, safer].forEach(function (impl) {
|
||||||
|
t.equal(typeof impl.Buffer.allocUnsafe, 'undefined')
|
||||||
|
t.equal(typeof impl.Buffer.allocUnsafeSlow, 'undefined')
|
||||||
|
});
|
||||||
|
[dangerous].forEach(function (impl) {
|
||||||
|
t.equal(typeof impl.Buffer.allocUnsafe, 'function')
|
||||||
|
t.equal(typeof impl.Buffer.allocUnsafeSlow, 'function')
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Generic methods/properties are defined and equal', function (t) {
|
||||||
|
['poolSize', 'isBuffer', 'concat', 'byteLength'].forEach(function (method) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl.Buffer[method], buffer.Buffer[method], method)
|
||||||
|
t.notEqual(typeof impl.Buffer[method], 'undefined', method)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Built-in buffer static methods/properties are inherited', function (t) {
|
||||||
|
Object.keys(buffer).forEach(function (method) {
|
||||||
|
if (method === 'SlowBuffer' || method === 'Buffer') return;
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl[method], buffer[method], method)
|
||||||
|
t.notEqual(typeof impl[method], 'undefined', method)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Built-in Buffer static methods/properties are inherited', function (t) {
|
||||||
|
Object.keys(buffer.Buffer).forEach(function (method) {
|
||||||
|
if (method === 'allocUnsafe' || method === 'allocUnsafeSlow') return;
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl.Buffer[method], buffer.Buffer[method], method)
|
||||||
|
t.notEqual(typeof impl.Buffer[method], 'undefined', method)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('.prototype property of Buffer is inherited', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl.Buffer.prototype, buffer.Buffer.prototype, 'prototype')
|
||||||
|
t.notEqual(typeof impl.Buffer.prototype, 'undefined', 'prototype')
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('All Safer methods are present in Dangerous', function (t) {
|
||||||
|
Object.keys(safer).forEach(function (method) {
|
||||||
|
if (method === 'Buffer') return;
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl[method], safer[method], method)
|
||||||
|
if (method !== 'kStringMaxLength') {
|
||||||
|
t.notEqual(typeof impl[method], 'undefined', method)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Object.keys(safer.Buffer).forEach(function (method) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl.Buffer[method], safer.Buffer[method], method)
|
||||||
|
t.notEqual(typeof impl.Buffer[method], 'undefined', method)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Safe methods from Dangerous methods are present in Safer', function (t) {
|
||||||
|
Object.keys(dangerous).forEach(function (method) {
|
||||||
|
if (method === 'Buffer') return;
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl[method], dangerous[method], method)
|
||||||
|
if (method !== 'kStringMaxLength') {
|
||||||
|
t.notEqual(typeof impl[method], 'undefined', method)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Object.keys(dangerous.Buffer).forEach(function (method) {
|
||||||
|
if (method === 'allocUnsafe' || method === 'allocUnsafeSlow') return;
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl.Buffer[method], dangerous.Buffer[method], method)
|
||||||
|
t.notEqual(typeof impl.Buffer[method], 'undefined', method)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Behaviour tests */
|
||||||
|
|
||||||
|
test('Methods return Buffers', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.alloc(0)))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.alloc(0, 10)))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.alloc(0, 'a')))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.alloc(10)))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.alloc(10, 'x')))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.alloc(9, 'ab')))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.from('')))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.from('string')))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.from('string', 'utf-8')))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.from('b25ldHdvdGhyZWU=', 'base64')))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.from([0, 42, 3])))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.from(new Uint8Array([0, 42, 3]))))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(impl.Buffer.from([])))
|
||||||
|
});
|
||||||
|
['allocUnsafe', 'allocUnsafeSlow'].forEach(function (method) {
|
||||||
|
t.ok(buffer.Buffer.isBuffer(dangerous.Buffer[method](0)))
|
||||||
|
t.ok(buffer.Buffer.isBuffer(dangerous.Buffer[method](10)))
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Constructor is buffer.Buffer', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl.Buffer.alloc(0).constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.alloc(0, 10).constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.alloc(0, 'a').constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.alloc(10).constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.alloc(10, 'x').constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.alloc(9, 'ab').constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.from('').constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.from('string').constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.from('string', 'utf-8').constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.from('b25ldHdvdGhyZWU=', 'base64').constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.from([0, 42, 3]).constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.from(new Uint8Array([0, 42, 3])).constructor, buffer.Buffer)
|
||||||
|
t.equal(impl.Buffer.from([]).constructor, buffer.Buffer)
|
||||||
|
});
|
||||||
|
[0, 10, 100].forEach(function (arg) {
|
||||||
|
t.equal(dangerous.Buffer.allocUnsafe(arg).constructor, buffer.Buffer)
|
||||||
|
t.equal(dangerous.Buffer.allocUnsafeSlow(arg).constructor, buffer.SlowBuffer(0).constructor)
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Invalid calls throw', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.throws(function () { impl.Buffer.from(0) })
|
||||||
|
t.throws(function () { impl.Buffer.from(10) })
|
||||||
|
t.throws(function () { impl.Buffer.from(10, 'utf-8') })
|
||||||
|
t.throws(function () { impl.Buffer.from('string', 'invalid encoding') })
|
||||||
|
t.throws(function () { impl.Buffer.from(-10) })
|
||||||
|
t.throws(function () { impl.Buffer.from(1e90) })
|
||||||
|
t.throws(function () { impl.Buffer.from(Infinity) })
|
||||||
|
t.throws(function () { impl.Buffer.from(-Infinity) })
|
||||||
|
t.throws(function () { impl.Buffer.from(NaN) })
|
||||||
|
t.throws(function () { impl.Buffer.from(null) })
|
||||||
|
t.throws(function () { impl.Buffer.from(undefined) })
|
||||||
|
t.throws(function () { impl.Buffer.from() })
|
||||||
|
t.throws(function () { impl.Buffer.from({}) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc('') })
|
||||||
|
t.throws(function () { impl.Buffer.alloc('string') })
|
||||||
|
t.throws(function () { impl.Buffer.alloc('string', 'utf-8') })
|
||||||
|
t.throws(function () { impl.Buffer.alloc('b25ldHdvdGhyZWU=', 'base64') })
|
||||||
|
t.throws(function () { impl.Buffer.alloc(-10) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc(1e90) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc(2 * (1 << 30)) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc(Infinity) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc(-Infinity) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc(null) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc(undefined) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc() })
|
||||||
|
t.throws(function () { impl.Buffer.alloc([]) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc([0, 42, 3]) })
|
||||||
|
t.throws(function () { impl.Buffer.alloc({}) })
|
||||||
|
});
|
||||||
|
['allocUnsafe', 'allocUnsafeSlow'].forEach(function (method) {
|
||||||
|
t.throws(function () { dangerous.Buffer[method]('') })
|
||||||
|
t.throws(function () { dangerous.Buffer[method]('string') })
|
||||||
|
t.throws(function () { dangerous.Buffer[method]('string', 'utf-8') })
|
||||||
|
t.throws(function () { dangerous.Buffer[method](2 * (1 << 30)) })
|
||||||
|
t.throws(function () { dangerous.Buffer[method](Infinity) })
|
||||||
|
if (dangerous.Buffer[method] === buffer.Buffer.allocUnsafe) {
|
||||||
|
t.skip('Skipping, older impl of allocUnsafe coerced negative sizes to 0')
|
||||||
|
} else {
|
||||||
|
t.throws(function () { dangerous.Buffer[method](-10) })
|
||||||
|
t.throws(function () { dangerous.Buffer[method](-1e90) })
|
||||||
|
t.throws(function () { dangerous.Buffer[method](-Infinity) })
|
||||||
|
}
|
||||||
|
t.throws(function () { dangerous.Buffer[method](null) })
|
||||||
|
t.throws(function () { dangerous.Buffer[method](undefined) })
|
||||||
|
t.throws(function () { dangerous.Buffer[method]() })
|
||||||
|
t.throws(function () { dangerous.Buffer[method]([]) })
|
||||||
|
t.throws(function () { dangerous.Buffer[method]([0, 42, 3]) })
|
||||||
|
t.throws(function () { dangerous.Buffer[method]({}) })
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Buffers have appropriate lengths', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.equal(impl.Buffer.alloc(0).length, 0)
|
||||||
|
t.equal(impl.Buffer.alloc(10).length, 10)
|
||||||
|
t.equal(impl.Buffer.from('').length, 0)
|
||||||
|
t.equal(impl.Buffer.from('string').length, 6)
|
||||||
|
t.equal(impl.Buffer.from('string', 'utf-8').length, 6)
|
||||||
|
t.equal(impl.Buffer.from('b25ldHdvdGhyZWU=', 'base64').length, 11)
|
||||||
|
t.equal(impl.Buffer.from([0, 42, 3]).length, 3)
|
||||||
|
t.equal(impl.Buffer.from(new Uint8Array([0, 42, 3])).length, 3)
|
||||||
|
t.equal(impl.Buffer.from([]).length, 0)
|
||||||
|
});
|
||||||
|
['allocUnsafe', 'allocUnsafeSlow'].forEach(function (method) {
|
||||||
|
t.equal(dangerous.Buffer[method](0).length, 0)
|
||||||
|
t.equal(dangerous.Buffer[method](10).length, 10)
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Buffers have appropriate lengths (2)', function (t) {
|
||||||
|
t.equal(index.Buffer.alloc, safer.Buffer.alloc)
|
||||||
|
t.equal(index.Buffer.alloc, dangerous.Buffer.alloc)
|
||||||
|
var ok = true;
|
||||||
|
[ safer.Buffer.alloc,
|
||||||
|
dangerous.Buffer.allocUnsafe,
|
||||||
|
dangerous.Buffer.allocUnsafeSlow
|
||||||
|
].forEach(function (method) {
|
||||||
|
for (var i = 0; i < 1e2; i++) {
|
||||||
|
var length = Math.round(Math.random() * 1e5)
|
||||||
|
var buf = method(length)
|
||||||
|
if (!buffer.Buffer.isBuffer(buf)) ok = false
|
||||||
|
if (buf.length !== length) ok = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.ok(ok)
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('.alloc(size) is zero-filled and has correct length', function (t) {
|
||||||
|
t.equal(index.Buffer.alloc, safer.Buffer.alloc)
|
||||||
|
t.equal(index.Buffer.alloc, dangerous.Buffer.alloc)
|
||||||
|
var ok = true
|
||||||
|
for (var i = 0; i < 1e2; i++) {
|
||||||
|
var length = Math.round(Math.random() * 2e6)
|
||||||
|
var buf = index.Buffer.alloc(length)
|
||||||
|
if (!buffer.Buffer.isBuffer(buf)) ok = false
|
||||||
|
if (buf.length !== length) ok = false
|
||||||
|
var j
|
||||||
|
for (j = 0; j < length; j++) {
|
||||||
|
if (buf[j] !== 0) ok = false
|
||||||
|
}
|
||||||
|
buf.fill(1)
|
||||||
|
for (j = 0; j < length; j++) {
|
||||||
|
if (buf[j] !== 1) ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.ok(ok)
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('.allocUnsafe / .allocUnsafeSlow are fillable and have correct lengths', function (t) {
|
||||||
|
['allocUnsafe', 'allocUnsafeSlow'].forEach(function (method) {
|
||||||
|
var ok = true
|
||||||
|
for (var i = 0; i < 1e2; i++) {
|
||||||
|
var length = Math.round(Math.random() * 2e6)
|
||||||
|
var buf = dangerous.Buffer[method](length)
|
||||||
|
if (!buffer.Buffer.isBuffer(buf)) ok = false
|
||||||
|
if (buf.length !== length) ok = false
|
||||||
|
buf.fill(0, 0, length)
|
||||||
|
var j
|
||||||
|
for (j = 0; j < length; j++) {
|
||||||
|
if (buf[j] !== 0) ok = false
|
||||||
|
}
|
||||||
|
buf.fill(1, 0, length)
|
||||||
|
for (j = 0; j < length; j++) {
|
||||||
|
if (buf[j] !== 1) ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.ok(ok, method)
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('.alloc(size, fill) is `fill`-filled', function (t) {
|
||||||
|
t.equal(index.Buffer.alloc, safer.Buffer.alloc)
|
||||||
|
t.equal(index.Buffer.alloc, dangerous.Buffer.alloc)
|
||||||
|
var ok = true
|
||||||
|
for (var i = 0; i < 1e2; i++) {
|
||||||
|
var length = Math.round(Math.random() * 2e6)
|
||||||
|
var fill = Math.round(Math.random() * 255)
|
||||||
|
var buf = index.Buffer.alloc(length, fill)
|
||||||
|
if (!buffer.Buffer.isBuffer(buf)) ok = false
|
||||||
|
if (buf.length !== length) ok = false
|
||||||
|
for (var j = 0; j < length; j++) {
|
||||||
|
if (buf[j] !== fill) ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.ok(ok)
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('.alloc(size, fill) is `fill`-filled', function (t) {
|
||||||
|
t.equal(index.Buffer.alloc, safer.Buffer.alloc)
|
||||||
|
t.equal(index.Buffer.alloc, dangerous.Buffer.alloc)
|
||||||
|
var ok = true
|
||||||
|
for (var i = 0; i < 1e2; i++) {
|
||||||
|
var length = Math.round(Math.random() * 2e6)
|
||||||
|
var fill = Math.round(Math.random() * 255)
|
||||||
|
var buf = index.Buffer.alloc(length, fill)
|
||||||
|
if (!buffer.Buffer.isBuffer(buf)) ok = false
|
||||||
|
if (buf.length !== length) ok = false
|
||||||
|
for (var j = 0; j < length; j++) {
|
||||||
|
if (buf[j] !== fill) ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.ok(ok)
|
||||||
|
t.deepEqual(index.Buffer.alloc(9, 'a'), index.Buffer.alloc(9, 97))
|
||||||
|
t.notDeepEqual(index.Buffer.alloc(9, 'a'), index.Buffer.alloc(9, 98))
|
||||||
|
|
||||||
|
var tmp = new buffer.Buffer(2)
|
||||||
|
tmp.fill('ok')
|
||||||
|
if (tmp[1] === tmp[0]) {
|
||||||
|
// Outdated Node.js
|
||||||
|
t.deepEqual(index.Buffer.alloc(5, 'ok'), index.Buffer.from('ooooo'))
|
||||||
|
} else {
|
||||||
|
t.deepEqual(index.Buffer.alloc(5, 'ok'), index.Buffer.from('okoko'))
|
||||||
|
}
|
||||||
|
t.notDeepEqual(index.Buffer.alloc(5, 'ok'), index.Buffer.from('kokok'))
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('safer.Buffer.from returns results same as Buffer constructor', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.deepEqual(impl.Buffer.from(''), new buffer.Buffer(''))
|
||||||
|
t.deepEqual(impl.Buffer.from('string'), new buffer.Buffer('string'))
|
||||||
|
t.deepEqual(impl.Buffer.from('string', 'utf-8'), new buffer.Buffer('string', 'utf-8'))
|
||||||
|
t.deepEqual(impl.Buffer.from('b25ldHdvdGhyZWU=', 'base64'), new buffer.Buffer('b25ldHdvdGhyZWU=', 'base64'))
|
||||||
|
t.deepEqual(impl.Buffer.from([0, 42, 3]), new buffer.Buffer([0, 42, 3]))
|
||||||
|
t.deepEqual(impl.Buffer.from(new Uint8Array([0, 42, 3])), new buffer.Buffer(new Uint8Array([0, 42, 3])))
|
||||||
|
t.deepEqual(impl.Buffer.from([]), new buffer.Buffer([]))
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('safer.Buffer.from returns consistent results', function (t) {
|
||||||
|
[index, safer, dangerous].forEach(function (impl) {
|
||||||
|
t.deepEqual(impl.Buffer.from(''), impl.Buffer.alloc(0))
|
||||||
|
t.deepEqual(impl.Buffer.from([]), impl.Buffer.alloc(0))
|
||||||
|
t.deepEqual(impl.Buffer.from(new Uint8Array([])), impl.Buffer.alloc(0))
|
||||||
|
t.deepEqual(impl.Buffer.from('string', 'utf-8'), impl.Buffer.from('string'))
|
||||||
|
t.deepEqual(impl.Buffer.from('string'), impl.Buffer.from([115, 116, 114, 105, 110, 103]))
|
||||||
|
t.deepEqual(impl.Buffer.from('string'), impl.Buffer.from(impl.Buffer.from('string')))
|
||||||
|
t.deepEqual(impl.Buffer.from('b25ldHdvdGhyZWU=', 'base64'), impl.Buffer.from('onetwothree'))
|
||||||
|
t.notDeepEqual(impl.Buffer.from('b25ldHdvdGhyZWU='), impl.Buffer.from('onetwothree'))
|
||||||
|
})
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
@ -0,0 +1,186 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v1.3.0](https://github.com/ljharb/get-intrinsic/compare/v1.2.7...v1.3.0) - 2025-02-22
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Dev Deps] update `es-abstract`, `es-value-fixtures`, `for-each`, `object-inspect` [`9b61553`](https://github.com/ljharb/get-intrinsic/commit/9b61553c587f1c1edbd435597e88c7d387da97dd)
|
||||||
|
- [Deps] update `call-bind-apply-helpers`, `es-object-atoms`, `get-proto` [`a341fee`](https://github.com/ljharb/get-intrinsic/commit/a341fee0f39a403b0f0069e82c97642d5eb11043)
|
||||||
|
- [New] add `Float16Array` [`de22116`](https://github.com/ljharb/get-intrinsic/commit/de22116b492fb989a0341bceb6e573abfaed73dc)
|
||||||
|
|
||||||
|
## [v1.2.7](https://github.com/ljharb/get-intrinsic/compare/v1.2.6...v1.2.7) - 2025-01-02
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Refactor] use `get-proto` directly [`00ab955`](https://github.com/ljharb/get-intrinsic/commit/00ab95546a0980c8ad42a84253daaa8d2adcedf9)
|
||||||
|
- [Deps] update `math-intrinsics` [`c716cdd`](https://github.com/ljharb/get-intrinsic/commit/c716cdd6bbe36b438057025561b8bb5a879ac8a0)
|
||||||
|
- [Dev Deps] update `call-bound`, `es-abstract` [`dc648a6`](https://github.com/ljharb/get-intrinsic/commit/dc648a67eb359037dff8d8619bfa71d86debccb1)
|
||||||
|
|
||||||
|
## [v1.2.6](https://github.com/ljharb/get-intrinsic/compare/v1.2.5...v1.2.6) - 2024-12-11
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Refactor] use `math-intrinsics` [`841be86`](https://github.com/ljharb/get-intrinsic/commit/841be8641a9254c4c75483b30c8871b5d5065926)
|
||||||
|
- [Refactor] use `es-object-atoms` [`42057df`](https://github.com/ljharb/get-intrinsic/commit/42057dfa16f66f64787e66482af381cc6f31d2c1)
|
||||||
|
- [Deps] update `call-bind-apply-helpers` [`45afa24`](https://github.com/ljharb/get-intrinsic/commit/45afa24a9ee4d6d3c172db1f555b16cb27843ef4)
|
||||||
|
- [Dev Deps] update `call-bound` [`9cba9c6`](https://github.com/ljharb/get-intrinsic/commit/9cba9c6e70212bc163b7a5529cb25df46071646f)
|
||||||
|
|
||||||
|
## [v1.2.5](https://github.com/ljharb/get-intrinsic/compare/v1.2.4...v1.2.5) - 2024-12-06
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [actions] split out node 10-20, and 20+ [`6e2b9dd`](https://github.com/ljharb/get-intrinsic/commit/6e2b9dd23902665681ebe453256ccfe21d7966f0)
|
||||||
|
- [Refactor] use `dunder-proto` and `call-bind-apply-helpers` instead of `has-proto` [`c095d17`](https://github.com/ljharb/get-intrinsic/commit/c095d179ad0f4fbfff20c8a3e0cb4fe668018998)
|
||||||
|
- [Refactor] use `gopd` [`9841d5b`](https://github.com/ljharb/get-intrinsic/commit/9841d5b35f7ab4fd2d193f0c741a50a077920e90)
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `es-abstract`, `es-value-fixtures`, `gopd`, `mock-property`, `object-inspect`, `tape` [`2d07e01`](https://github.com/ljharb/get-intrinsic/commit/2d07e01310cee2cbaedfead6903df128b1f5d425)
|
||||||
|
- [Deps] update `gopd`, `has-proto`, `has-symbols`, `hasown` [`974d8bf`](https://github.com/ljharb/get-intrinsic/commit/974d8bf5baad7939eef35c25cc1dd88c10a30fa6)
|
||||||
|
- [Dev Deps] update `call-bind`, `es-abstract`, `tape` [`df9dde1`](https://github.com/ljharb/get-intrinsic/commit/df9dde178186631ab8a3165ede056549918ce4bc)
|
||||||
|
- [Refactor] cache `es-define-property` as well [`43ef543`](https://github.com/ljharb/get-intrinsic/commit/43ef543cb02194401420e3a914a4ca9168691926)
|
||||||
|
- [Deps] update `has-proto`, `has-symbols`, `hasown` [`ad4949d`](https://github.com/ljharb/get-intrinsic/commit/ad4949d5467316505aad89bf75f9417ed782f7af)
|
||||||
|
- [Tests] use `call-bound` directly [`ad5c406`](https://github.com/ljharb/get-intrinsic/commit/ad5c4069774bfe90e520a35eead5fe5ca9d69e80)
|
||||||
|
- [Deps] update `has-proto`, `hasown` [`45414ca`](https://github.com/ljharb/get-intrinsic/commit/45414caa312333a2798953682c68f85c550627dd)
|
||||||
|
- [Tests] replace `aud` with `npm audit` [`18d3509`](https://github.com/ljharb/get-intrinsic/commit/18d3509f79460e7924da70409ee81e5053087523)
|
||||||
|
- [Deps] update `es-define-property` [`aadaa3b`](https://github.com/ljharb/get-intrinsic/commit/aadaa3b2188d77ad9bff394ce5d4249c49eb21f5)
|
||||||
|
- [Dev Deps] add missing peer dep [`c296a16`](https://github.com/ljharb/get-intrinsic/commit/c296a16246d0c9a5981944f4cc5cf61fbda0cf6a)
|
||||||
|
|
||||||
|
## [v1.2.4](https://github.com/ljharb/get-intrinsic/compare/v1.2.3...v1.2.4) - 2024-02-05
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Refactor] use all 7 <+ ES6 Errors from `es-errors` [`bcac811`](https://github.com/ljharb/get-intrinsic/commit/bcac811abdc1c982e12abf848a410d6aae148d14)
|
||||||
|
|
||||||
|
## [v1.2.3](https://github.com/ljharb/get-intrinsic/compare/v1.2.2...v1.2.3) - 2024-02-03
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Refactor] use `es-errors`, so things that only need those do not need `get-intrinsic` [`f11db9c`](https://github.com/ljharb/get-intrinsic/commit/f11db9c4fb97d87bbd53d3c73ac6b3db3613ad3b)
|
||||||
|
- [Dev Deps] update `aud`, `es-abstract`, `mock-property`, `npmignore` [`b7ac7d1`](https://github.com/ljharb/get-intrinsic/commit/b7ac7d1616fefb03877b1aed0c8f8d61aad32b6c)
|
||||||
|
- [meta] simplify `exports` [`faa0cc6`](https://github.com/ljharb/get-intrinsic/commit/faa0cc618e2830ffb51a8202490b0c215d965cbc)
|
||||||
|
- [meta] add missing `engines.node` [`774dd0b`](https://github.com/ljharb/get-intrinsic/commit/774dd0b3e8f741c3f05a6322d124d6087f146af1)
|
||||||
|
- [Dev Deps] update `tape` [`5828e8e`](https://github.com/ljharb/get-intrinsic/commit/5828e8e4a04e69312e87a36c0ea39428a7a4c3d8)
|
||||||
|
- [Robustness] use null objects for lookups [`eb9a11f`](https://github.com/ljharb/get-intrinsic/commit/eb9a11fa9eb3e13b193fcc05a7fb814341b1a7b7)
|
||||||
|
- [meta] add `sideEffects` flag [`89bcc7a`](https://github.com/ljharb/get-intrinsic/commit/89bcc7a42e19bf07b7c21e3094d5ab177109e6d2)
|
||||||
|
|
||||||
|
## [v1.2.2](https://github.com/ljharb/get-intrinsic/compare/v1.2.1...v1.2.2) - 2023-10-20
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `call-bind`, `es-abstract`, `mock-property`, `object-inspect`, `tape` [`f51bcf2`](https://github.com/ljharb/get-intrinsic/commit/f51bcf26412d58d17ce17c91c9afd0ad271f0762)
|
||||||
|
- [Refactor] use `hasown` instead of `has` [`18d14b7`](https://github.com/ljharb/get-intrinsic/commit/18d14b799bea6b5765e1cec91890830cbcdb0587)
|
||||||
|
- [Deps] update `function-bind` [`6e109c8`](https://github.com/ljharb/get-intrinsic/commit/6e109c81e03804cc5e7824fb64353cdc3d8ee2c7)
|
||||||
|
|
||||||
|
## [v1.2.1](https://github.com/ljharb/get-intrinsic/compare/v1.2.0...v1.2.1) - 2023-05-13
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Fix] avoid a crash in envs without `__proto__` [`7bad8d0`](https://github.com/ljharb/get-intrinsic/commit/7bad8d061bf8721733b58b73a2565af2b6756b64)
|
||||||
|
- [Dev Deps] update `es-abstract` [`c60e6b7`](https://github.com/ljharb/get-intrinsic/commit/c60e6b7b4cf9660c7f27ed970970fd55fac48dc5)
|
||||||
|
|
||||||
|
## [v1.2.0](https://github.com/ljharb/get-intrinsic/compare/v1.1.3...v1.2.0) - 2023-01-19
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [actions] update checkout action [`ca6b12f`](https://github.com/ljharb/get-intrinsic/commit/ca6b12f31eaacea4ea3b055e744cd61623385ffb)
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`, `es-abstract`, `object-inspect`, `tape` [`41a3727`](https://github.com/ljharb/get-intrinsic/commit/41a3727d0026fa04273ae216a5f8e12eefd72da8)
|
||||||
|
- [Fix] ensure `Error.prototype` is undeniable [`c511e97`](https://github.com/ljharb/get-intrinsic/commit/c511e97ae99c764c4524b540dee7a70757af8da3)
|
||||||
|
- [Dev Deps] update `aud`, `es-abstract`, `tape` [`1bef8a8`](https://github.com/ljharb/get-intrinsic/commit/1bef8a8fd439ebb80863199b6189199e0851ac67)
|
||||||
|
- [Dev Deps] update `aud`, `es-abstract` [`0d41f16`](https://github.com/ljharb/get-intrinsic/commit/0d41f16bcd500bc28b7bfc98043ebf61ea081c26)
|
||||||
|
- [New] add `BigInt64Array` and `BigUint64Array` [`a6cca25`](https://github.com/ljharb/get-intrinsic/commit/a6cca25f29635889b7e9bd669baf9e04be90e48c)
|
||||||
|
- [Tests] use `gopd` [`ecf7722`](https://github.com/ljharb/get-intrinsic/commit/ecf7722240d15cfd16edda06acf63359c10fb9bd)
|
||||||
|
|
||||||
|
## [v1.1.3](https://github.com/ljharb/get-intrinsic/compare/v1.1.2...v1.1.3) - 2022-09-12
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Dev Deps] update `es-abstract`, `es-value-fixtures`, `tape` [`07ff291`](https://github.com/ljharb/get-intrinsic/commit/07ff291816406ebe5a12d7f16965bde0942dd688)
|
||||||
|
- [Fix] properly check for % signs [`50ac176`](https://github.com/ljharb/get-intrinsic/commit/50ac1760fe99c227e64eabde76e9c0e44cd881b5)
|
||||||
|
|
||||||
|
## [v1.1.2](https://github.com/ljharb/get-intrinsic/compare/v1.1.1...v1.1.2) - 2022-06-08
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [Fix] properly validate against extra % signs [`#16`](https://github.com/ljharb/get-intrinsic/issues/16)
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [actions] reuse common workflows [`0972547`](https://github.com/ljharb/get-intrinsic/commit/0972547efd0abc863fe4c445a6ca7eb4f8c6901d)
|
||||||
|
- [meta] use `npmignore` to autogenerate an npmignore file [`5ba0b51`](https://github.com/ljharb/get-intrinsic/commit/5ba0b51d8d8d4f1c31d426d74abc0770fd106bad)
|
||||||
|
- [actions] use `node/install` instead of `node/run`; use `codecov` action [`c364492`](https://github.com/ljharb/get-intrinsic/commit/c364492af4af51333e6f81c0bf21fd3d602c3661)
|
||||||
|
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog`, `es-abstract`, `object-inspect`, `tape` [`dc04dad`](https://github.com/ljharb/get-intrinsic/commit/dc04dad86f6e5608775a2640cb0db5927ae29ed9)
|
||||||
|
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `es-abstract`, `object-inspect`, `safe-publish-latest`, `tape` [`1c14059`](https://github.com/ljharb/get-intrinsic/commit/1c1405984e86dd2dc9366c15d8a0294a96a146a5)
|
||||||
|
- [Tests] use `mock-property` [`b396ef0`](https://github.com/ljharb/get-intrinsic/commit/b396ef05bb73b1d699811abd64b0d9b97997fdda)
|
||||||
|
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog`, `object-inspect`, `tape` [`c2c758d`](https://github.com/ljharb/get-intrinsic/commit/c2c758d3b90af4fef0a76910d8d3c292ec8d1d3e)
|
||||||
|
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `es-abstract`, `es-value-fixtures`, `object-inspect`, `tape` [`29e3c09`](https://github.com/ljharb/get-intrinsic/commit/29e3c091c2bf3e17099969847e8729d0e46896de)
|
||||||
|
- [actions] update codecov uploader [`8cbc141`](https://github.com/ljharb/get-intrinsic/commit/8cbc1418940d7a8941f3a7985cbc4ac095c5e13d)
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`, `es-abstract`, `es-value-fixtures`, `object-inspect`, `tape` [`10b6f5c`](https://github.com/ljharb/get-intrinsic/commit/10b6f5c02593fb3680c581d696ac124e30652932)
|
||||||
|
- [readme] add github actions/codecov badges [`4e25400`](https://github.com/ljharb/get-intrinsic/commit/4e25400d9f51ae9eb059cbe22d9144e70ea214e8)
|
||||||
|
- [Tests] use `for-each` instead of `foreach` [`c05b957`](https://github.com/ljharb/get-intrinsic/commit/c05b957ad9a7bc7721af7cc9e9be1edbfe057496)
|
||||||
|
- [Dev Deps] update `es-abstract` [`29b05ae`](https://github.com/ljharb/get-intrinsic/commit/29b05aec3e7330e9ad0b8e0f685a9112c20cdd97)
|
||||||
|
- [meta] use `prepublishOnly` script for npm 7+ [`95c285d`](https://github.com/ljharb/get-intrinsic/commit/95c285da810516057d3bbfa871176031af38f05d)
|
||||||
|
- [Deps] update `has-symbols` [`593cb4f`](https://github.com/ljharb/get-intrinsic/commit/593cb4fb38e7922e40e42c183f45274b636424cd)
|
||||||
|
- [readme] fix repo URLs [`1c8305b`](https://github.com/ljharb/get-intrinsic/commit/1c8305b5365827c9b6fc785434aac0e1328ff2f5)
|
||||||
|
- [Deps] update `has-symbols` [`c7138b6`](https://github.com/ljharb/get-intrinsic/commit/c7138b6c6d73132d859471fb8c13304e1e7c8b20)
|
||||||
|
- [Dev Deps] remove unused `has-bigints` [`bd63aff`](https://github.com/ljharb/get-intrinsic/commit/bd63aff6ad8f3a986c557fcda2914187bdaab359)
|
||||||
|
|
||||||
|
## [v1.1.1](https://github.com/ljharb/get-intrinsic/compare/v1.1.0...v1.1.1) - 2021-02-03
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [meta] export `./package.json` [`#9`](https://github.com/ljharb/get-intrinsic/issues/9)
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [readme] flesh out the readme; use `evalmd` [`d12f12c`](https://github.com/ljharb/get-intrinsic/commit/d12f12c15345a0a0772cc65a7c64369529abd614)
|
||||||
|
- [eslint] set up proper globals config [`5a8c098`](https://github.com/ljharb/get-intrinsic/commit/5a8c0984e3319d1ac0e64b102f8ec18b64e79f36)
|
||||||
|
- [Dev Deps] update `eslint` [`7b9a5c0`](https://github.com/ljharb/get-intrinsic/commit/7b9a5c0d31a90ca1a1234181c74988fb046701cd)
|
||||||
|
|
||||||
|
## [v1.1.0](https://github.com/ljharb/get-intrinsic/compare/v1.0.2...v1.1.0) - 2021-01-25
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [Refactor] delay `Function` eval until syntax-derived values are requested [`#3`](https://github.com/ljharb/get-intrinsic/issues/3)
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Tests] migrate tests to Github Actions [`2ab762b`](https://github.com/ljharb/get-intrinsic/commit/2ab762b48164aea8af37a40ba105bbc8246ab8c4)
|
||||||
|
- [meta] do not publish github action workflow files [`5e7108e`](https://github.com/ljharb/get-intrinsic/commit/5e7108e4768b244d48d9567ba4f8a6cab9c65b8e)
|
||||||
|
- [Tests] add some coverage [`01ac7a8`](https://github.com/ljharb/get-intrinsic/commit/01ac7a87ac29738567e8524cd8c9e026b1fa8cb3)
|
||||||
|
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `call-bind`, `es-abstract`, `tape`; add `call-bind` [`911b672`](https://github.com/ljharb/get-intrinsic/commit/911b672fbffae433a96924c6ce013585e425f4b7)
|
||||||
|
- [Refactor] rearrange evalled constructors a bit [`7e7e4bf`](https://github.com/ljharb/get-intrinsic/commit/7e7e4bf583f3799c8ac1c6c5e10d2cb553957347)
|
||||||
|
- [meta] add Automatic Rebase and Require Allow Edits workflows [`0199968`](https://github.com/ljharb/get-intrinsic/commit/01999687a263ffce0a3cb011dfbcb761754aedbc)
|
||||||
|
|
||||||
|
## [v1.0.2](https://github.com/ljharb/get-intrinsic/compare/v1.0.1...v1.0.2) - 2020-12-17
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Fix] Throw for non‑existent intrinsics [`68f873b`](https://github.com/ljharb/get-intrinsic/commit/68f873b013c732a05ad6f5fc54f697e55515461b)
|
||||||
|
- [Fix] Throw for non‑existent segments in the intrinsic path [`8325dee`](https://github.com/ljharb/get-intrinsic/commit/8325deee43128f3654d3399aa9591741ebe17b21)
|
||||||
|
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-bigints`, `object-inspect` [`0c227a7`](https://github.com/ljharb/get-intrinsic/commit/0c227a7d8b629166f25715fd242553892e458525)
|
||||||
|
- [meta] do not lint coverage output [`70d2419`](https://github.com/ljharb/get-intrinsic/commit/70d24199b620043cd9110fc5f426d214ebe21dc9)
|
||||||
|
|
||||||
|
## [v1.0.1](https://github.com/ljharb/get-intrinsic/compare/v1.0.0...v1.0.1) - 2020-10-30
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Tests] gather coverage data on every job [`d1d280d`](https://github.com/ljharb/get-intrinsic/commit/d1d280dec714e3f0519cc877dbcb193057d9cac6)
|
||||||
|
- [Fix] add missing dependencies [`5031771`](https://github.com/ljharb/get-intrinsic/commit/5031771bb1095b38be88ce7c41d5de88718e432e)
|
||||||
|
- [Tests] use `es-value-fixtures` [`af48765`](https://github.com/ljharb/get-intrinsic/commit/af48765a23c5323fb0b6b38dbf00eb5099c7bebc)
|
||||||
|
|
||||||
|
## v1.0.0 - 2020-10-29
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- Implementation [`bbce57c`](https://github.com/ljharb/get-intrinsic/commit/bbce57c6f33d05b2d8d3efa273ceeb3ee01127bb)
|
||||||
|
- Tests [`17b4f0d`](https://github.com/ljharb/get-intrinsic/commit/17b4f0d56dea6b4059b56fc30ef3ee4d9500ebc2)
|
||||||
|
- Initial commit [`3153294`](https://github.com/ljharb/get-intrinsic/commit/31532948de363b0a27dd9fd4649e7b7028ec4b44)
|
||||||
|
- npm init [`fb326c4`](https://github.com/ljharb/get-intrinsic/commit/fb326c4d2817c8419ec31de1295f06bb268a7902)
|
||||||
|
- [meta] add Automatic Rebase and Require Allow Edits workflows [`48862fb`](https://github.com/ljharb/get-intrinsic/commit/48862fb2508c8f6a57968e6d08b7c883afc9d550)
|
||||||
|
- [meta] add `auto-changelog` [`5f28ad0`](https://github.com/ljharb/get-intrinsic/commit/5f28ad019e060a353d8028f9f2591a9cc93074a1)
|
||||||
|
- [meta] add "funding"; create `FUNDING.yml` [`c2bbdde`](https://github.com/ljharb/get-intrinsic/commit/c2bbddeba73a875be61484ee4680b129a6d4e0a1)
|
||||||
|
- [Tests] add `npm run lint` [`0a84b98`](https://github.com/ljharb/get-intrinsic/commit/0a84b98b22b7cf7a748666f705b0003a493c35fd)
|
||||||
|
- Only apps should have lockfiles [`9586c75`](https://github.com/ljharb/get-intrinsic/commit/9586c75866c1ee678e4d5d4dbbdef6997e511b05)
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
|
||||||
|
"extends": "@ljharb",
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
"new-cap": ["error", {
|
||||||
|
"capIsNewExceptions": [
|
||||||
|
"GetIntrinsic",
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
# has-flag [](https://travis-ci.org/sindresorhus/has-flag)
|
||||||
|
|
||||||
|
> Check if [`argv`](https://nodejs.org/docs/latest/api/process.html#process_process_argv) has a specific flag
|
||||||
|
|
||||||
|
Correctly stops looking after an `--` argument terminator.
|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm install has-flag
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
// foo.js
|
||||||
|
const hasFlag = require('has-flag');
|
||||||
|
|
||||||
|
hasFlag('unicorn');
|
||||||
|
//=> true
|
||||||
|
|
||||||
|
hasFlag('--unicorn');
|
||||||
|
//=> true
|
||||||
|
|
||||||
|
hasFlag('f');
|
||||||
|
//=> true
|
||||||
|
|
||||||
|
hasFlag('-f');
|
||||||
|
//=> true
|
||||||
|
|
||||||
|
hasFlag('foo=bar');
|
||||||
|
//=> true
|
||||||
|
|
||||||
|
hasFlag('foo');
|
||||||
|
//=> false
|
||||||
|
|
||||||
|
hasFlag('rainbow');
|
||||||
|
//=> false
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ node foo.js -f --unicorn --foo=bar -- --rainbow
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### hasFlag(flag, [argv])
|
||||||
|
|
||||||
|
Returns a boolean for whether the flag exists.
|
||||||
|
|
||||||
|
#### flag
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
CLI flag to look for. The `--` prefix is optional.
|
||||||
|
|
||||||
|
#### argv
|
||||||
|
|
||||||
|
Type: `string[]`<br>
|
||||||
|
Default: `process.argv`
|
||||||
|
|
||||||
|
CLI arguments.
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © [Sindre Sorhus](https://sindresorhus.com)
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var test = require('tape');
|
||||||
|
|
||||||
|
var callBound = require('../');
|
||||||
|
|
||||||
|
/** @template {true} T @template U @typedef {T extends U ? T : never} AssertType */
|
||||||
|
|
||||||
|
test('callBound', function (t) {
|
||||||
|
// static primitive
|
||||||
|
t.equal(callBound('Array.length'), Array.length, 'Array.length yields itself');
|
||||||
|
t.equal(callBound('%Array.length%'), Array.length, '%Array.length% yields itself');
|
||||||
|
|
||||||
|
// static non-function object
|
||||||
|
t.equal(callBound('Array.prototype'), Array.prototype, 'Array.prototype yields itself');
|
||||||
|
t.equal(callBound('%Array.prototype%'), Array.prototype, '%Array.prototype% yields itself');
|
||||||
|
t.equal(callBound('Array.constructor'), Array.constructor, 'Array.constructor yields itself');
|
||||||
|
t.equal(callBound('%Array.constructor%'), Array.constructor, '%Array.constructor% yields itself');
|
||||||
|
|
||||||
|
// static function
|
||||||
|
t.equal(callBound('Date.parse'), Date.parse, 'Date.parse yields itself');
|
||||||
|
t.equal(callBound('%Date.parse%'), Date.parse, '%Date.parse% yields itself');
|
||||||
|
|
||||||
|
// prototype primitive
|
||||||
|
t.equal(callBound('Error.prototype.message'), Error.prototype.message, 'Error.prototype.message yields itself');
|
||||||
|
t.equal(callBound('%Error.prototype.message%'), Error.prototype.message, '%Error.prototype.message% yields itself');
|
||||||
|
|
||||||
|
var x = callBound('Object.prototype.toString');
|
||||||
|
var y = callBound('%Object.prototype.toString%');
|
||||||
|
|
||||||
|
// prototype function
|
||||||
|
t.notEqual(x, Object.prototype.toString, 'Object.prototype.toString does not yield itself');
|
||||||
|
t.notEqual(y, Object.prototype.toString, '%Object.prototype.toString% does not yield itself');
|
||||||
|
t.equal(x(true), Object.prototype.toString.call(true), 'call-bound Object.prototype.toString calls into the original');
|
||||||
|
t.equal(y(true), Object.prototype.toString.call(true), 'call-bound %Object.prototype.toString% calls into the original');
|
||||||
|
|
||||||
|
t['throws'](
|
||||||
|
// @ts-expect-error
|
||||||
|
function () { callBound('does not exist'); },
|
||||||
|
SyntaxError,
|
||||||
|
'nonexistent intrinsic throws'
|
||||||
|
);
|
||||||
|
t['throws'](
|
||||||
|
// @ts-expect-error
|
||||||
|
function () { callBound('does not exist', true); },
|
||||||
|
SyntaxError,
|
||||||
|
'allowMissing arg still throws for unknown intrinsic'
|
||||||
|
);
|
||||||
|
|
||||||
|
t.test('real but absent intrinsic', { skip: typeof WeakRef !== 'undefined' }, function (st) {
|
||||||
|
st['throws'](
|
||||||
|
function () { callBound('WeakRef'); },
|
||||||
|
TypeError,
|
||||||
|
'real but absent intrinsic throws'
|
||||||
|
);
|
||||||
|
st.equal(callBound('WeakRef', true), undefined, 'allowMissing arg avoids exception');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
# side-channel-weakmap <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
|
||||||
|
|
||||||
|
[![github actions][actions-image]][actions-url]
|
||||||
|
[![coverage][codecov-image]][codecov-url]
|
||||||
|
[![License][license-image]][license-url]
|
||||||
|
[![Downloads][downloads-image]][downloads-url]
|
||||||
|
|
||||||
|
[![npm badge][npm-badge-png]][package-url]
|
||||||
|
|
||||||
|
Store information about any JS value in a side channel. Uses WeakMap if available.
|
||||||
|
|
||||||
|
Warning: this implementation will leak memory until you `delete` the `key`.
|
||||||
|
Use [`side-channel`](https://npmjs.com/side-channel) for the best available strategy.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save side-channel-weakmap
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage/Examples
|
||||||
|
|
||||||
|
```js
|
||||||
|
const assert = require('assert');
|
||||||
|
const getSideChannelList = require('side-channel-weakmap');
|
||||||
|
|
||||||
|
const channel = getSideChannelList();
|
||||||
|
|
||||||
|
const key = {};
|
||||||
|
assert.equal(channel.has(key), false);
|
||||||
|
assert.throws(() => channel.assert(key), TypeError);
|
||||||
|
|
||||||
|
channel.set(key, 42);
|
||||||
|
|
||||||
|
channel.assert(key); // does not throw
|
||||||
|
assert.equal(channel.has(key), true);
|
||||||
|
assert.equal(channel.get(key), 42);
|
||||||
|
|
||||||
|
channel.delete(key);
|
||||||
|
assert.equal(channel.has(key), false);
|
||||||
|
assert.throws(() => channel.assert(key), TypeError);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Clone the repo, `npm install`, and run `npm test`
|
||||||
|
|
||||||
|
[package-url]: https://npmjs.org/package/side-channel-weakmap
|
||||||
|
[npm-version-svg]: https://versionbadg.es/ljharb/side-channel-weakmap.svg
|
||||||
|
[deps-svg]: https://david-dm.org/ljharb/side-channel-weakmap.svg
|
||||||
|
[deps-url]: https://david-dm.org/ljharb/side-channel-weakmap
|
||||||
|
[dev-deps-svg]: https://david-dm.org/ljharb/side-channel-weakmap/dev-status.svg
|
||||||
|
[dev-deps-url]: https://david-dm.org/ljharb/side-channel-weakmap#info=devDependencies
|
||||||
|
[npm-badge-png]: https://nodei.co/npm/side-channel-weakmap.png?downloads=true&stars=true
|
||||||
|
[license-image]: https://img.shields.io/npm/l/side-channel-weakmap.svg
|
||||||
|
[license-url]: LICENSE
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/side-channel-weakmap.svg
|
||||||
|
[downloads-url]: https://npm-stat.com/charts.html?package=side-channel-weakmap
|
||||||
|
[codecov-image]: https://codecov.io/gh/ljharb/side-channel-weakmap/branch/main/graphs/badge.svg
|
||||||
|
[codecov-url]: https://app.codecov.io/gh/ljharb/side-channel-weakmap/
|
||||||
|
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/side-channel-weakmap
|
||||||
|
[actions-url]: https://github.com/ljharb/side-channel-weakmap/actions
|
||||||
@ -0,0 +1,287 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const { Readable } = require('stream');
|
||||||
|
const sysPath = require('path');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
|
||||||
|
const readdir = promisify(fs.readdir);
|
||||||
|
const stat = promisify(fs.stat);
|
||||||
|
const lstat = promisify(fs.lstat);
|
||||||
|
const realpath = promisify(fs.realpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} EntryInfo
|
||||||
|
* @property {String} path
|
||||||
|
* @property {String} fullPath
|
||||||
|
* @property {fs.Stats=} stats
|
||||||
|
* @property {fs.Dirent=} dirent
|
||||||
|
* @property {String} basename
|
||||||
|
*/
|
||||||
|
|
||||||
|
const BANG = '!';
|
||||||
|
const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';
|
||||||
|
const NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);
|
||||||
|
const FILE_TYPE = 'files';
|
||||||
|
const DIR_TYPE = 'directories';
|
||||||
|
const FILE_DIR_TYPE = 'files_directories';
|
||||||
|
const EVERYTHING_TYPE = 'all';
|
||||||
|
const ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE];
|
||||||
|
|
||||||
|
const isNormalFlowError = error => NORMAL_FLOW_ERRORS.has(error.code);
|
||||||
|
const [maj, min] = process.versions.node.split('.').slice(0, 2).map(n => Number.parseInt(n, 10));
|
||||||
|
const wantBigintFsStats = process.platform === 'win32' && (maj > 10 || (maj === 10 && min >= 5));
|
||||||
|
|
||||||
|
const normalizeFilter = filter => {
|
||||||
|
if (filter === undefined) return;
|
||||||
|
if (typeof filter === 'function') return filter;
|
||||||
|
|
||||||
|
if (typeof filter === 'string') {
|
||||||
|
const glob = picomatch(filter.trim());
|
||||||
|
return entry => glob(entry.basename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(filter)) {
|
||||||
|
const positive = [];
|
||||||
|
const negative = [];
|
||||||
|
for (const item of filter) {
|
||||||
|
const trimmed = item.trim();
|
||||||
|
if (trimmed.charAt(0) === BANG) {
|
||||||
|
negative.push(picomatch(trimmed.slice(1)));
|
||||||
|
} else {
|
||||||
|
positive.push(picomatch(trimmed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (negative.length > 0) {
|
||||||
|
if (positive.length > 0) {
|
||||||
|
return entry =>
|
||||||
|
positive.some(f => f(entry.basename)) && !negative.some(f => f(entry.basename));
|
||||||
|
}
|
||||||
|
return entry => !negative.some(f => f(entry.basename));
|
||||||
|
}
|
||||||
|
return entry => positive.some(f => f(entry.basename));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReaddirpStream extends Readable {
|
||||||
|
static get defaultOptions() {
|
||||||
|
return {
|
||||||
|
root: '.',
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
fileFilter: (path) => true,
|
||||||
|
directoryFilter: (path) => true,
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
type: FILE_TYPE,
|
||||||
|
lstat: false,
|
||||||
|
depth: 2147483648,
|
||||||
|
alwaysStat: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(options = {}) {
|
||||||
|
super({
|
||||||
|
objectMode: true,
|
||||||
|
autoDestroy: true,
|
||||||
|
highWaterMark: options.highWaterMark || 4096
|
||||||
|
});
|
||||||
|
const opts = { ...ReaddirpStream.defaultOptions, ...options };
|
||||||
|
const { root, type } = opts;
|
||||||
|
|
||||||
|
this._fileFilter = normalizeFilter(opts.fileFilter);
|
||||||
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
||||||
|
|
||||||
|
const statMethod = opts.lstat ? lstat : stat;
|
||||||
|
// Use bigint stats if it's windows and stat() supports options (node 10+).
|
||||||
|
if (wantBigintFsStats) {
|
||||||
|
this._stat = path => statMethod(path, { bigint: true });
|
||||||
|
} else {
|
||||||
|
this._stat = statMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._maxDepth = opts.depth;
|
||||||
|
this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);
|
||||||
|
this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);
|
||||||
|
this._wantsEverything = type === EVERYTHING_TYPE;
|
||||||
|
this._root = sysPath.resolve(root);
|
||||||
|
this._isDirent = ('Dirent' in fs) && !opts.alwaysStat;
|
||||||
|
this._statsProp = this._isDirent ? 'dirent' : 'stats';
|
||||||
|
this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };
|
||||||
|
|
||||||
|
// Launch stream with one parent, the root dir.
|
||||||
|
this.parents = [this._exploreDir(root, 1)];
|
||||||
|
this.reading = false;
|
||||||
|
this.parent = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _read(batch) {
|
||||||
|
if (this.reading) return;
|
||||||
|
this.reading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (!this.destroyed && batch > 0) {
|
||||||
|
const { path, depth, files = [] } = this.parent || {};
|
||||||
|
|
||||||
|
if (files.length > 0) {
|
||||||
|
const slice = files.splice(0, batch).map(dirent => this._formatEntry(dirent, path));
|
||||||
|
for (const entry of await Promise.all(slice)) {
|
||||||
|
if (this.destroyed) return;
|
||||||
|
|
||||||
|
const entryType = await this._getEntryType(entry);
|
||||||
|
if (entryType === 'directory' && this._directoryFilter(entry)) {
|
||||||
|
if (depth <= this._maxDepth) {
|
||||||
|
this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._wantsDir) {
|
||||||
|
this.push(entry);
|
||||||
|
batch--;
|
||||||
|
}
|
||||||
|
} else if ((entryType === 'file' || this._includeAsFile(entry)) && this._fileFilter(entry)) {
|
||||||
|
if (this._wantsFile) {
|
||||||
|
this.push(entry);
|
||||||
|
batch--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const parent = this.parents.pop();
|
||||||
|
if (!parent) {
|
||||||
|
this.push(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.parent = await parent;
|
||||||
|
if (this.destroyed) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.destroy(error);
|
||||||
|
} finally {
|
||||||
|
this.reading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _exploreDir(path, depth) {
|
||||||
|
let files;
|
||||||
|
try {
|
||||||
|
files = await readdir(path, this._rdOptions);
|
||||||
|
} catch (error) {
|
||||||
|
this._onError(error);
|
||||||
|
}
|
||||||
|
return { files, depth, path };
|
||||||
|
}
|
||||||
|
|
||||||
|
async _formatEntry(dirent, path) {
|
||||||
|
let entry;
|
||||||
|
try {
|
||||||
|
const basename = this._isDirent ? dirent.name : dirent;
|
||||||
|
const fullPath = sysPath.resolve(sysPath.join(path, basename));
|
||||||
|
entry = { path: sysPath.relative(this._root, fullPath), fullPath, basename };
|
||||||
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
||||||
|
} catch (err) {
|
||||||
|
this._onError(err);
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onError(err) {
|
||||||
|
if (isNormalFlowError(err) && !this.destroyed) {
|
||||||
|
this.emit('warn', err);
|
||||||
|
} else {
|
||||||
|
this.destroy(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _getEntryType(entry) {
|
||||||
|
// entry may be undefined, because a warning or an error were emitted
|
||||||
|
// and the statsProp is undefined
|
||||||
|
const stats = entry && entry[this._statsProp];
|
||||||
|
if (!stats) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stats.isFile()) {
|
||||||
|
return 'file';
|
||||||
|
}
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
return 'directory';
|
||||||
|
}
|
||||||
|
if (stats && stats.isSymbolicLink()) {
|
||||||
|
const full = entry.fullPath;
|
||||||
|
try {
|
||||||
|
const entryRealPath = await realpath(full);
|
||||||
|
const entryRealPathStats = await lstat(entryRealPath);
|
||||||
|
if (entryRealPathStats.isFile()) {
|
||||||
|
return 'file';
|
||||||
|
}
|
||||||
|
if (entryRealPathStats.isDirectory()) {
|
||||||
|
const len = entryRealPath.length;
|
||||||
|
if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) {
|
||||||
|
const recursiveError = new Error(
|
||||||
|
`Circular symlink detected: "${full}" points to "${entryRealPath}"`
|
||||||
|
);
|
||||||
|
recursiveError.code = RECURSIVE_ERROR_CODE;
|
||||||
|
return this._onError(recursiveError);
|
||||||
|
}
|
||||||
|
return 'directory';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this._onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_includeAsFile(entry) {
|
||||||
|
const stats = entry && entry[this._statsProp];
|
||||||
|
|
||||||
|
return stats && this._wantsEverything && !stats.isDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ReaddirpArguments
|
||||||
|
* @property {Function=} fileFilter
|
||||||
|
* @property {Function=} directoryFilter
|
||||||
|
* @property {String=} type
|
||||||
|
* @property {Number=} depth
|
||||||
|
* @property {String=} root
|
||||||
|
* @property {Boolean=} lstat
|
||||||
|
* @property {Boolean=} bigint
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function which ends up calling readdirRec and reads all files and directories in given root recursively.
|
||||||
|
* @param {String} root Root directory
|
||||||
|
* @param {ReaddirpArguments=} options Options to specify root (start directory), filters and recursion depth
|
||||||
|
*/
|
||||||
|
const readdirp = (root, options = {}) => {
|
||||||
|
let type = options.entryType || options.type;
|
||||||
|
if (type === 'both') type = FILE_DIR_TYPE; // backwards-compatibility
|
||||||
|
if (type) options.type = type;
|
||||||
|
if (!root) {
|
||||||
|
throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');
|
||||||
|
} else if (typeof root !== 'string') {
|
||||||
|
throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');
|
||||||
|
} else if (type && !ALL_TYPES.includes(type)) {
|
||||||
|
throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.root = root;
|
||||||
|
return new ReaddirpStream(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const readdirpPromise = (root, options = {}) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const files = [];
|
||||||
|
readdirp(root, options)
|
||||||
|
.on('data', entry => files.push(entry))
|
||||||
|
.on('end', () => resolve(files))
|
||||||
|
.on('error', error => reject(error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
readdirp.promise = readdirpPromise;
|
||||||
|
readdirp.ReaddirpStream = ReaddirpStream;
|
||||||
|
readdirp.default = readdirp;
|
||||||
|
|
||||||
|
module.exports = readdirp;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const parse = require('./parse')
|
||||||
|
const valid = (version, options) => {
|
||||||
|
const v = parse(version, options)
|
||||||
|
return v ? v.version : null
|
||||||
|
}
|
||||||
|
module.exports = valid
|
||||||
@ -0,0 +1,182 @@
|
|||||||
|
[
|
||||||
|
["0","\u0000",127],
|
||||||
|
["8ea1","。",62],
|
||||||
|
["a1a1"," 、。,.・:;?!゛゜´`¨^ ̄_ヽヾゝゞ〃仝々〆〇ー―‐/\~∥|…‥‘’“”()〔〕[]{}〈",9,"+-±×÷=≠<>≦≧∞∴♂♀°′″℃¥$¢£%#&*@§☆★○●◎◇"],
|
||||||
|
["a2a1","◆□■△▲▽▼※〒→←↑↓〓"],
|
||||||
|
["a2ba","∈∋⊆⊇⊂⊃∪∩"],
|
||||||
|
["a2ca","∧∨¬⇒⇔∀∃"],
|
||||||
|
["a2dc","∠⊥⌒∂∇≡≒≪≫√∽∝∵∫∬"],
|
||||||
|
["a2f2","ʼn♯♭♪†‡¶"],
|
||||||
|
["a2fe","◯"],
|
||||||
|
["a3b0","0",9],
|
||||||
|
["a3c1","A",25],
|
||||||
|
["a3e1","a",25],
|
||||||
|
["a4a1","ぁ",82],
|
||||||
|
["a5a1","ァ",85],
|
||||||
|
["a6a1","Α",16,"Σ",6],
|
||||||
|
["a6c1","α",16,"σ",6],
|
||||||
|
["a7a1","А",5,"ЁЖ",25],
|
||||||
|
["a7d1","а",5,"ёж",25],
|
||||||
|
["a8a1","─│┌┐┘└├┬┤┴┼━┃┏┓┛┗┣┳┫┻╋┠┯┨┷┿┝┰┥┸╂"],
|
||||||
|
["ada1","①",19,"Ⅰ",9],
|
||||||
|
["adc0","㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡"],
|
||||||
|
["addf","㍻〝〟№㏍℡㊤",4,"㈱㈲㈹㍾㍽㍼≒≡∫∮∑√⊥∠∟⊿∵∩∪"],
|
||||||
|
["b0a1","亜唖娃阿哀愛挨姶逢葵茜穐悪握渥旭葦芦鯵梓圧斡扱宛姐虻飴絢綾鮎或粟袷安庵按暗案闇鞍杏以伊位依偉囲夷委威尉惟意慰易椅為畏異移維緯胃萎衣謂違遺医井亥域育郁磯一壱溢逸稲茨芋鰯允印咽員因姻引飲淫胤蔭"],
|
||||||
|
["b1a1","院陰隠韻吋右宇烏羽迂雨卯鵜窺丑碓臼渦嘘唄欝蔚鰻姥厩浦瓜閏噂云運雲荏餌叡営嬰影映曳栄永泳洩瑛盈穎頴英衛詠鋭液疫益駅悦謁越閲榎厭円園堰奄宴延怨掩援沿演炎焔煙燕猿縁艶苑薗遠鉛鴛塩於汚甥凹央奥往応"],
|
||||||
|
["b2a1","押旺横欧殴王翁襖鴬鴎黄岡沖荻億屋憶臆桶牡乙俺卸恩温穏音下化仮何伽価佳加可嘉夏嫁家寡科暇果架歌河火珂禍禾稼箇花苛茄荷華菓蝦課嘩貨迦過霞蚊俄峨我牙画臥芽蛾賀雅餓駕介会解回塊壊廻快怪悔恢懐戒拐改"],
|
||||||
|
["b3a1","魁晦械海灰界皆絵芥蟹開階貝凱劾外咳害崖慨概涯碍蓋街該鎧骸浬馨蛙垣柿蛎鈎劃嚇各廓拡撹格核殻獲確穫覚角赫較郭閣隔革学岳楽額顎掛笠樫橿梶鰍潟割喝恰括活渇滑葛褐轄且鰹叶椛樺鞄株兜竃蒲釜鎌噛鴨栢茅萱"],
|
||||||
|
["b4a1","粥刈苅瓦乾侃冠寒刊勘勧巻喚堪姦完官寛干幹患感慣憾換敢柑桓棺款歓汗漢澗潅環甘監看竿管簡緩缶翰肝艦莞観諌貫還鑑間閑関陥韓館舘丸含岸巌玩癌眼岩翫贋雁頑顔願企伎危喜器基奇嬉寄岐希幾忌揮机旗既期棋棄"],
|
||||||
|
["b5a1","機帰毅気汽畿祈季稀紀徽規記貴起軌輝飢騎鬼亀偽儀妓宜戯技擬欺犠疑祇義蟻誼議掬菊鞠吉吃喫桔橘詰砧杵黍却客脚虐逆丘久仇休及吸宮弓急救朽求汲泣灸球究窮笈級糾給旧牛去居巨拒拠挙渠虚許距鋸漁禦魚亨享京"],
|
||||||
|
["b6a1","供侠僑兇競共凶協匡卿叫喬境峡強彊怯恐恭挟教橋況狂狭矯胸脅興蕎郷鏡響饗驚仰凝尭暁業局曲極玉桐粁僅勤均巾錦斤欣欽琴禁禽筋緊芹菌衿襟謹近金吟銀九倶句区狗玖矩苦躯駆駈駒具愚虞喰空偶寓遇隅串櫛釧屑屈"],
|
||||||
|
["b7a1","掘窟沓靴轡窪熊隈粂栗繰桑鍬勲君薫訓群軍郡卦袈祁係傾刑兄啓圭珪型契形径恵慶慧憩掲携敬景桂渓畦稽系経継繋罫茎荊蛍計詣警軽頚鶏芸迎鯨劇戟撃激隙桁傑欠決潔穴結血訣月件倹倦健兼券剣喧圏堅嫌建憲懸拳捲"],
|
||||||
|
["b8a1","検権牽犬献研硯絹県肩見謙賢軒遣鍵険顕験鹸元原厳幻弦減源玄現絃舷言諺限乎個古呼固姑孤己庫弧戸故枯湖狐糊袴股胡菰虎誇跨鈷雇顧鼓五互伍午呉吾娯後御悟梧檎瑚碁語誤護醐乞鯉交佼侯候倖光公功効勾厚口向"],
|
||||||
|
["b9a1","后喉坑垢好孔孝宏工巧巷幸広庚康弘恒慌抗拘控攻昂晃更杭校梗構江洪浩港溝甲皇硬稿糠紅紘絞綱耕考肯肱腔膏航荒行衡講貢購郊酵鉱砿鋼閤降項香高鴻剛劫号合壕拷濠豪轟麹克刻告国穀酷鵠黒獄漉腰甑忽惚骨狛込"],
|
||||||
|
["baa1","此頃今困坤墾婚恨懇昏昆根梱混痕紺艮魂些佐叉唆嵯左差査沙瑳砂詐鎖裟坐座挫債催再最哉塞妻宰彩才採栽歳済災采犀砕砦祭斎細菜裁載際剤在材罪財冴坂阪堺榊肴咲崎埼碕鷺作削咋搾昨朔柵窄策索錯桜鮭笹匙冊刷"],
|
||||||
|
["bba1","察拶撮擦札殺薩雑皐鯖捌錆鮫皿晒三傘参山惨撒散桟燦珊産算纂蚕讃賛酸餐斬暫残仕仔伺使刺司史嗣四士始姉姿子屍市師志思指支孜斯施旨枝止死氏獅祉私糸紙紫肢脂至視詞詩試誌諮資賜雌飼歯事似侍児字寺慈持時"],
|
||||||
|
["bca1","次滋治爾璽痔磁示而耳自蒔辞汐鹿式識鴫竺軸宍雫七叱執失嫉室悉湿漆疾質実蔀篠偲柴芝屡蕊縞舎写射捨赦斜煮社紗者謝車遮蛇邪借勺尺杓灼爵酌釈錫若寂弱惹主取守手朱殊狩珠種腫趣酒首儒受呪寿授樹綬需囚収周"],
|
||||||
|
["bda1","宗就州修愁拾洲秀秋終繍習臭舟蒐衆襲讐蹴輯週酋酬集醜什住充十従戎柔汁渋獣縦重銃叔夙宿淑祝縮粛塾熟出術述俊峻春瞬竣舜駿准循旬楯殉淳準潤盾純巡遵醇順処初所暑曙渚庶緒署書薯藷諸助叙女序徐恕鋤除傷償"],
|
||||||
|
["bea1","勝匠升召哨商唱嘗奨妾娼宵将小少尚庄床廠彰承抄招掌捷昇昌昭晶松梢樟樵沼消渉湘焼焦照症省硝礁祥称章笑粧紹肖菖蒋蕉衝裳訟証詔詳象賞醤鉦鍾鐘障鞘上丈丞乗冗剰城場壌嬢常情擾条杖浄状畳穣蒸譲醸錠嘱埴飾"],
|
||||||
|
["bfa1","拭植殖燭織職色触食蝕辱尻伸信侵唇娠寝審心慎振新晋森榛浸深申疹真神秦紳臣芯薪親診身辛進針震人仁刃塵壬尋甚尽腎訊迅陣靭笥諏須酢図厨逗吹垂帥推水炊睡粋翠衰遂酔錐錘随瑞髄崇嵩数枢趨雛据杉椙菅頗雀裾"],
|
||||||
|
["c0a1","澄摺寸世瀬畝是凄制勢姓征性成政整星晴棲栖正清牲生盛精聖声製西誠誓請逝醒青静斉税脆隻席惜戚斥昔析石積籍績脊責赤跡蹟碩切拙接摂折設窃節説雪絶舌蝉仙先千占宣専尖川戦扇撰栓栴泉浅洗染潜煎煽旋穿箭線"],
|
||||||
|
["c1a1","繊羨腺舛船薦詮賎践選遷銭銑閃鮮前善漸然全禅繕膳糎噌塑岨措曾曽楚狙疏疎礎祖租粗素組蘇訴阻遡鼠僧創双叢倉喪壮奏爽宋層匝惣想捜掃挿掻操早曹巣槍槽漕燥争痩相窓糟総綜聡草荘葬蒼藻装走送遭鎗霜騒像増憎"],
|
||||||
|
["c2a1","臓蔵贈造促側則即息捉束測足速俗属賊族続卒袖其揃存孫尊損村遜他多太汰詑唾堕妥惰打柁舵楕陀駄騨体堆対耐岱帯待怠態戴替泰滞胎腿苔袋貸退逮隊黛鯛代台大第醍題鷹滝瀧卓啄宅托択拓沢濯琢託鐸濁諾茸凧蛸只"],
|
||||||
|
["c3a1","叩但達辰奪脱巽竪辿棚谷狸鱈樽誰丹単嘆坦担探旦歎淡湛炭短端箪綻耽胆蛋誕鍛団壇弾断暖檀段男談値知地弛恥智池痴稚置致蜘遅馳築畜竹筑蓄逐秩窒茶嫡着中仲宙忠抽昼柱注虫衷註酎鋳駐樗瀦猪苧著貯丁兆凋喋寵"],
|
||||||
|
["c4a1","帖帳庁弔張彫徴懲挑暢朝潮牒町眺聴脹腸蝶調諜超跳銚長頂鳥勅捗直朕沈珍賃鎮陳津墜椎槌追鎚痛通塚栂掴槻佃漬柘辻蔦綴鍔椿潰坪壷嬬紬爪吊釣鶴亭低停偵剃貞呈堤定帝底庭廷弟悌抵挺提梯汀碇禎程締艇訂諦蹄逓"],
|
||||||
|
["c5a1","邸鄭釘鼎泥摘擢敵滴的笛適鏑溺哲徹撤轍迭鉄典填天展店添纏甜貼転顛点伝殿澱田電兎吐堵塗妬屠徒斗杜渡登菟賭途都鍍砥砺努度土奴怒倒党冬凍刀唐塔塘套宕島嶋悼投搭東桃梼棟盗淘湯涛灯燈当痘祷等答筒糖統到"],
|
||||||
|
["c6a1","董蕩藤討謄豆踏逃透鐙陶頭騰闘働動同堂導憧撞洞瞳童胴萄道銅峠鴇匿得徳涜特督禿篤毒独読栃橡凸突椴届鳶苫寅酉瀞噸屯惇敦沌豚遁頓呑曇鈍奈那内乍凪薙謎灘捺鍋楢馴縄畷南楠軟難汝二尼弐迩匂賑肉虹廿日乳入"],
|
||||||
|
["c7a1","如尿韮任妊忍認濡禰祢寧葱猫熱年念捻撚燃粘乃廼之埜嚢悩濃納能脳膿農覗蚤巴把播覇杷波派琶破婆罵芭馬俳廃拝排敗杯盃牌背肺輩配倍培媒梅楳煤狽買売賠陪這蝿秤矧萩伯剥博拍柏泊白箔粕舶薄迫曝漠爆縛莫駁麦"],
|
||||||
|
["c8a1","函箱硲箸肇筈櫨幡肌畑畠八鉢溌発醗髪伐罰抜筏閥鳩噺塙蛤隼伴判半反叛帆搬斑板氾汎版犯班畔繁般藩販範釆煩頒飯挽晩番盤磐蕃蛮匪卑否妃庇彼悲扉批披斐比泌疲皮碑秘緋罷肥被誹費避非飛樋簸備尾微枇毘琵眉美"],
|
||||||
|
["c9a1","鼻柊稗匹疋髭彦膝菱肘弼必畢筆逼桧姫媛紐百謬俵彪標氷漂瓢票表評豹廟描病秒苗錨鋲蒜蛭鰭品彬斌浜瀕貧賓頻敏瓶不付埠夫婦富冨布府怖扶敷斧普浮父符腐膚芙譜負賦赴阜附侮撫武舞葡蕪部封楓風葺蕗伏副復幅服"],
|
||||||
|
["caa1","福腹複覆淵弗払沸仏物鮒分吻噴墳憤扮焚奮粉糞紛雰文聞丙併兵塀幣平弊柄並蔽閉陛米頁僻壁癖碧別瞥蔑箆偏変片篇編辺返遍便勉娩弁鞭保舗鋪圃捕歩甫補輔穂募墓慕戊暮母簿菩倣俸包呆報奉宝峰峯崩庖抱捧放方朋"],
|
||||||
|
["cba1","法泡烹砲縫胞芳萌蓬蜂褒訪豊邦鋒飽鳳鵬乏亡傍剖坊妨帽忘忙房暴望某棒冒紡肪膨謀貌貿鉾防吠頬北僕卜墨撲朴牧睦穆釦勃没殆堀幌奔本翻凡盆摩磨魔麻埋妹昧枚毎哩槙幕膜枕鮪柾鱒桝亦俣又抹末沫迄侭繭麿万慢満"],
|
||||||
|
["cca1","漫蔓味未魅巳箕岬密蜜湊蓑稔脈妙粍民眠務夢無牟矛霧鵡椋婿娘冥名命明盟迷銘鳴姪牝滅免棉綿緬面麺摸模茂妄孟毛猛盲網耗蒙儲木黙目杢勿餅尤戻籾貰問悶紋門匁也冶夜爺耶野弥矢厄役約薬訳躍靖柳薮鑓愉愈油癒"],
|
||||||
|
["cda1","諭輸唯佑優勇友宥幽悠憂揖有柚湧涌猶猷由祐裕誘遊邑郵雄融夕予余与誉輿預傭幼妖容庸揚揺擁曜楊様洋溶熔用窯羊耀葉蓉要謡踊遥陽養慾抑欲沃浴翌翼淀羅螺裸来莱頼雷洛絡落酪乱卵嵐欄濫藍蘭覧利吏履李梨理璃"],
|
||||||
|
["cea1","痢裏裡里離陸律率立葎掠略劉流溜琉留硫粒隆竜龍侶慮旅虜了亮僚両凌寮料梁涼猟療瞭稜糧良諒遼量陵領力緑倫厘林淋燐琳臨輪隣鱗麟瑠塁涙累類令伶例冷励嶺怜玲礼苓鈴隷零霊麗齢暦歴列劣烈裂廉恋憐漣煉簾練聯"],
|
||||||
|
["cfa1","蓮連錬呂魯櫓炉賂路露労婁廊弄朗楼榔浪漏牢狼篭老聾蝋郎六麓禄肋録論倭和話歪賄脇惑枠鷲亙亘鰐詫藁蕨椀湾碗腕"],
|
||||||
|
["d0a1","弌丐丕个丱丶丼丿乂乖乘亂亅豫亊舒弍于亞亟亠亢亰亳亶从仍仄仆仂仗仞仭仟价伉佚估佛佝佗佇佶侈侏侘佻佩佰侑佯來侖儘俔俟俎俘俛俑俚俐俤俥倚倨倔倪倥倅伜俶倡倩倬俾俯們倆偃假會偕偐偈做偖偬偸傀傚傅傴傲"],
|
||||||
|
["d1a1","僉僊傳僂僖僞僥僭僣僮價僵儉儁儂儖儕儔儚儡儺儷儼儻儿兀兒兌兔兢竸兩兪兮冀冂囘册冉冏冑冓冕冖冤冦冢冩冪冫决冱冲冰况冽凅凉凛几處凩凭凰凵凾刄刋刔刎刧刪刮刳刹剏剄剋剌剞剔剪剴剩剳剿剽劍劔劒剱劈劑辨"],
|
||||||
|
["d2a1","辧劬劭劼劵勁勍勗勞勣勦飭勠勳勵勸勹匆匈甸匍匐匏匕匚匣匯匱匳匸區卆卅丗卉卍凖卞卩卮夘卻卷厂厖厠厦厥厮厰厶參簒雙叟曼燮叮叨叭叺吁吽呀听吭吼吮吶吩吝呎咏呵咎呟呱呷呰咒呻咀呶咄咐咆哇咢咸咥咬哄哈咨"],
|
||||||
|
["d3a1","咫哂咤咾咼哘哥哦唏唔哽哮哭哺哢唹啀啣啌售啜啅啖啗唸唳啝喙喀咯喊喟啻啾喘喞單啼喃喩喇喨嗚嗅嗟嗄嗜嗤嗔嘔嗷嘖嗾嗽嘛嗹噎噐營嘴嘶嘲嘸噫噤嘯噬噪嚆嚀嚊嚠嚔嚏嚥嚮嚶嚴囂嚼囁囃囀囈囎囑囓囗囮囹圀囿圄圉"],
|
||||||
|
["d4a1","圈國圍圓團圖嗇圜圦圷圸坎圻址坏坩埀垈坡坿垉垓垠垳垤垪垰埃埆埔埒埓堊埖埣堋堙堝塲堡塢塋塰毀塒堽塹墅墹墟墫墺壞墻墸墮壅壓壑壗壙壘壥壜壤壟壯壺壹壻壼壽夂夊夐夛梦夥夬夭夲夸夾竒奕奐奎奚奘奢奠奧奬奩"],
|
||||||
|
["d5a1","奸妁妝佞侫妣妲姆姨姜妍姙姚娥娟娑娜娉娚婀婬婉娵娶婢婪媚媼媾嫋嫂媽嫣嫗嫦嫩嫖嫺嫻嬌嬋嬖嬲嫐嬪嬶嬾孃孅孀孑孕孚孛孥孩孰孳孵學斈孺宀它宦宸寃寇寉寔寐寤實寢寞寥寫寰寶寳尅將專對尓尠尢尨尸尹屁屆屎屓"],
|
||||||
|
["d6a1","屐屏孱屬屮乢屶屹岌岑岔妛岫岻岶岼岷峅岾峇峙峩峽峺峭嶌峪崋崕崗嵜崟崛崑崔崢崚崙崘嵌嵒嵎嵋嵬嵳嵶嶇嶄嶂嶢嶝嶬嶮嶽嶐嶷嶼巉巍巓巒巖巛巫已巵帋帚帙帑帛帶帷幄幃幀幎幗幔幟幢幤幇幵并幺麼广庠廁廂廈廐廏"],
|
||||||
|
["d7a1","廖廣廝廚廛廢廡廨廩廬廱廳廰廴廸廾弃弉彝彜弋弑弖弩弭弸彁彈彌彎弯彑彖彗彙彡彭彳彷徃徂彿徊很徑徇從徙徘徠徨徭徼忖忻忤忸忱忝悳忿怡恠怙怐怩怎怱怛怕怫怦怏怺恚恁恪恷恟恊恆恍恣恃恤恂恬恫恙悁悍惧悃悚"],
|
||||||
|
["d8a1","悄悛悖悗悒悧悋惡悸惠惓悴忰悽惆悵惘慍愕愆惶惷愀惴惺愃愡惻惱愍愎慇愾愨愧慊愿愼愬愴愽慂慄慳慷慘慙慚慫慴慯慥慱慟慝慓慵憙憖憇憬憔憚憊憑憫憮懌懊應懷懈懃懆憺懋罹懍懦懣懶懺懴懿懽懼懾戀戈戉戍戌戔戛"],
|
||||||
|
["d9a1","戞戡截戮戰戲戳扁扎扞扣扛扠扨扼抂抉找抒抓抖拔抃抔拗拑抻拏拿拆擔拈拜拌拊拂拇抛拉挌拮拱挧挂挈拯拵捐挾捍搜捏掖掎掀掫捶掣掏掉掟掵捫捩掾揩揀揆揣揉插揶揄搖搴搆搓搦搶攝搗搨搏摧摯摶摎攪撕撓撥撩撈撼"],
|
||||||
|
["daa1","據擒擅擇撻擘擂擱擧舉擠擡抬擣擯攬擶擴擲擺攀擽攘攜攅攤攣攫攴攵攷收攸畋效敖敕敍敘敞敝敲數斂斃變斛斟斫斷旃旆旁旄旌旒旛旙无旡旱杲昊昃旻杳昵昶昴昜晏晄晉晁晞晝晤晧晨晟晢晰暃暈暎暉暄暘暝曁暹曉暾暼"],
|
||||||
|
["dba1","曄暸曖曚曠昿曦曩曰曵曷朏朖朞朦朧霸朮朿朶杁朸朷杆杞杠杙杣杤枉杰枩杼杪枌枋枦枡枅枷柯枴柬枳柩枸柤柞柝柢柮枹柎柆柧檜栞框栩桀桍栲桎梳栫桙档桷桿梟梏梭梔條梛梃檮梹桴梵梠梺椏梍桾椁棊椈棘椢椦棡椌棍"],
|
||||||
|
["dca1","棔棧棕椶椒椄棗棣椥棹棠棯椨椪椚椣椡棆楹楷楜楸楫楔楾楮椹楴椽楙椰楡楞楝榁楪榲榮槐榿槁槓榾槎寨槊槝榻槃榧樮榑榠榜榕榴槞槨樂樛槿權槹槲槧樅榱樞槭樔槫樊樒櫁樣樓橄樌橲樶橸橇橢橙橦橈樸樢檐檍檠檄檢檣"],
|
||||||
|
["dda1","檗蘗檻櫃櫂檸檳檬櫞櫑櫟檪櫚櫪櫻欅蘖櫺欒欖鬱欟欸欷盜欹飮歇歃歉歐歙歔歛歟歡歸歹歿殀殄殃殍殘殕殞殤殪殫殯殲殱殳殷殼毆毋毓毟毬毫毳毯麾氈氓气氛氤氣汞汕汢汪沂沍沚沁沛汾汨汳沒沐泄泱泓沽泗泅泝沮沱沾"],
|
||||||
|
["dea1","沺泛泯泙泪洟衍洶洫洽洸洙洵洳洒洌浣涓浤浚浹浙涎涕濤涅淹渕渊涵淇淦涸淆淬淞淌淨淒淅淺淙淤淕淪淮渭湮渮渙湲湟渾渣湫渫湶湍渟湃渺湎渤滿渝游溂溪溘滉溷滓溽溯滄溲滔滕溏溥滂溟潁漑灌滬滸滾漿滲漱滯漲滌"],
|
||||||
|
["dfa1","漾漓滷澆潺潸澁澀潯潛濳潭澂潼潘澎澑濂潦澳澣澡澤澹濆澪濟濕濬濔濘濱濮濛瀉瀋濺瀑瀁瀏濾瀛瀚潴瀝瀘瀟瀰瀾瀲灑灣炙炒炯烱炬炸炳炮烟烋烝烙焉烽焜焙煥煕熈煦煢煌煖煬熏燻熄熕熨熬燗熹熾燒燉燔燎燠燬燧燵燼"],
|
||||||
|
["e0a1","燹燿爍爐爛爨爭爬爰爲爻爼爿牀牆牋牘牴牾犂犁犇犒犖犢犧犹犲狃狆狄狎狒狢狠狡狹狷倏猗猊猜猖猝猴猯猩猥猾獎獏默獗獪獨獰獸獵獻獺珈玳珎玻珀珥珮珞璢琅瑯琥珸琲琺瑕琿瑟瑙瑁瑜瑩瑰瑣瑪瑶瑾璋璞璧瓊瓏瓔珱"],
|
||||||
|
["e1a1","瓠瓣瓧瓩瓮瓲瓰瓱瓸瓷甄甃甅甌甎甍甕甓甞甦甬甼畄畍畊畉畛畆畚畩畤畧畫畭畸當疆疇畴疊疉疂疔疚疝疥疣痂疳痃疵疽疸疼疱痍痊痒痙痣痞痾痿痼瘁痰痺痲痳瘋瘍瘉瘟瘧瘠瘡瘢瘤瘴瘰瘻癇癈癆癜癘癡癢癨癩癪癧癬癰"],
|
||||||
|
["e2a1","癲癶癸發皀皃皈皋皎皖皓皙皚皰皴皸皹皺盂盍盖盒盞盡盥盧盪蘯盻眈眇眄眩眤眞眥眦眛眷眸睇睚睨睫睛睥睿睾睹瞎瞋瞑瞠瞞瞰瞶瞹瞿瞼瞽瞻矇矍矗矚矜矣矮矼砌砒礦砠礪硅碎硴碆硼碚碌碣碵碪碯磑磆磋磔碾碼磅磊磬"],
|
||||||
|
["e3a1","磧磚磽磴礇礒礑礙礬礫祀祠祗祟祚祕祓祺祿禊禝禧齋禪禮禳禹禺秉秕秧秬秡秣稈稍稘稙稠稟禀稱稻稾稷穃穗穉穡穢穩龝穰穹穽窈窗窕窘窖窩竈窰窶竅竄窿邃竇竊竍竏竕竓站竚竝竡竢竦竭竰笂笏笊笆笳笘笙笞笵笨笶筐"],
|
||||||
|
["e4a1","筺笄筍笋筌筅筵筥筴筧筰筱筬筮箝箘箟箍箜箚箋箒箏筝箙篋篁篌篏箴篆篝篩簑簔篦篥籠簀簇簓篳篷簗簍篶簣簧簪簟簷簫簽籌籃籔籏籀籐籘籟籤籖籥籬籵粃粐粤粭粢粫粡粨粳粲粱粮粹粽糀糅糂糘糒糜糢鬻糯糲糴糶糺紆"],
|
||||||
|
["e5a1","紂紜紕紊絅絋紮紲紿紵絆絳絖絎絲絨絮絏絣經綉絛綏絽綛綺綮綣綵緇綽綫總綢綯緜綸綟綰緘緝緤緞緻緲緡縅縊縣縡縒縱縟縉縋縢繆繦縻縵縹繃縷縲縺繧繝繖繞繙繚繹繪繩繼繻纃緕繽辮繿纈纉續纒纐纓纔纖纎纛纜缸缺"],
|
||||||
|
["e6a1","罅罌罍罎罐网罕罔罘罟罠罨罩罧罸羂羆羃羈羇羌羔羞羝羚羣羯羲羹羮羶羸譱翅翆翊翕翔翡翦翩翳翹飜耆耄耋耒耘耙耜耡耨耿耻聊聆聒聘聚聟聢聨聳聲聰聶聹聽聿肄肆肅肛肓肚肭冐肬胛胥胙胝胄胚胖脉胯胱脛脩脣脯腋"],
|
||||||
|
["e7a1","隋腆脾腓腑胼腱腮腥腦腴膃膈膊膀膂膠膕膤膣腟膓膩膰膵膾膸膽臀臂膺臉臍臑臙臘臈臚臟臠臧臺臻臾舁舂舅與舊舍舐舖舩舫舸舳艀艙艘艝艚艟艤艢艨艪艫舮艱艷艸艾芍芒芫芟芻芬苡苣苟苒苴苳苺莓范苻苹苞茆苜茉苙"],
|
||||||
|
["e8a1","茵茴茖茲茱荀茹荐荅茯茫茗茘莅莚莪莟莢莖茣莎莇莊荼莵荳荵莠莉莨菴萓菫菎菽萃菘萋菁菷萇菠菲萍萢萠莽萸蔆菻葭萪萼蕚蒄葷葫蒭葮蒂葩葆萬葯葹萵蓊葢蒹蒿蒟蓙蓍蒻蓚蓐蓁蓆蓖蒡蔡蓿蓴蔗蔘蔬蔟蔕蔔蓼蕀蕣蕘蕈"],
|
||||||
|
["e9a1","蕁蘂蕋蕕薀薤薈薑薊薨蕭薔薛藪薇薜蕷蕾薐藉薺藏薹藐藕藝藥藜藹蘊蘓蘋藾藺蘆蘢蘚蘰蘿虍乕虔號虧虱蚓蚣蚩蚪蚋蚌蚶蚯蛄蛆蚰蛉蠣蚫蛔蛞蛩蛬蛟蛛蛯蜒蜆蜈蜀蜃蛻蜑蜉蜍蛹蜊蜴蜿蜷蜻蜥蜩蜚蝠蝟蝸蝌蝎蝴蝗蝨蝮蝙"],
|
||||||
|
["eaa1","蝓蝣蝪蠅螢螟螂螯蟋螽蟀蟐雖螫蟄螳蟇蟆螻蟯蟲蟠蠏蠍蟾蟶蟷蠎蟒蠑蠖蠕蠢蠡蠱蠶蠹蠧蠻衄衂衒衙衞衢衫袁衾袞衵衽袵衲袂袗袒袮袙袢袍袤袰袿袱裃裄裔裘裙裝裹褂裼裴裨裲褄褌褊褓襃褞褥褪褫襁襄褻褶褸襌褝襠襞"],
|
||||||
|
["eba1","襦襤襭襪襯襴襷襾覃覈覊覓覘覡覩覦覬覯覲覺覽覿觀觚觜觝觧觴觸訃訖訐訌訛訝訥訶詁詛詒詆詈詼詭詬詢誅誂誄誨誡誑誥誦誚誣諄諍諂諚諫諳諧諤諱謔諠諢諷諞諛謌謇謚諡謖謐謗謠謳鞫謦謫謾謨譁譌譏譎證譖譛譚譫"],
|
||||||
|
["eca1","譟譬譯譴譽讀讌讎讒讓讖讙讚谺豁谿豈豌豎豐豕豢豬豸豺貂貉貅貊貍貎貔豼貘戝貭貪貽貲貳貮貶賈賁賤賣賚賽賺賻贄贅贊贇贏贍贐齎贓賍贔贖赧赭赱赳趁趙跂趾趺跏跚跖跌跛跋跪跫跟跣跼踈踉跿踝踞踐踟蹂踵踰踴蹊"],
|
||||||
|
["eda1","蹇蹉蹌蹐蹈蹙蹤蹠踪蹣蹕蹶蹲蹼躁躇躅躄躋躊躓躑躔躙躪躡躬躰軆躱躾軅軈軋軛軣軼軻軫軾輊輅輕輒輙輓輜輟輛輌輦輳輻輹轅轂輾轌轉轆轎轗轜轢轣轤辜辟辣辭辯辷迚迥迢迪迯邇迴逅迹迺逑逕逡逍逞逖逋逧逶逵逹迸"],
|
||||||
|
["eea1","遏遐遑遒逎遉逾遖遘遞遨遯遶隨遲邂遽邁邀邊邉邏邨邯邱邵郢郤扈郛鄂鄒鄙鄲鄰酊酖酘酣酥酩酳酲醋醉醂醢醫醯醪醵醴醺釀釁釉釋釐釖釟釡釛釼釵釶鈞釿鈔鈬鈕鈑鉞鉗鉅鉉鉤鉈銕鈿鉋鉐銜銖銓銛鉚鋏銹銷鋩錏鋺鍄錮"],
|
||||||
|
["efa1","錙錢錚錣錺錵錻鍜鍠鍼鍮鍖鎰鎬鎭鎔鎹鏖鏗鏨鏥鏘鏃鏝鏐鏈鏤鐚鐔鐓鐃鐇鐐鐶鐫鐵鐡鐺鑁鑒鑄鑛鑠鑢鑞鑪鈩鑰鑵鑷鑽鑚鑼鑾钁鑿閂閇閊閔閖閘閙閠閨閧閭閼閻閹閾闊濶闃闍闌闕闔闖關闡闥闢阡阨阮阯陂陌陏陋陷陜陞"],
|
||||||
|
["f0a1","陝陟陦陲陬隍隘隕隗險隧隱隲隰隴隶隸隹雎雋雉雍襍雜霍雕雹霄霆霈霓霎霑霏霖霙霤霪霰霹霽霾靄靆靈靂靉靜靠靤靦靨勒靫靱靹鞅靼鞁靺鞆鞋鞏鞐鞜鞨鞦鞣鞳鞴韃韆韈韋韜韭齏韲竟韶韵頏頌頸頤頡頷頽顆顏顋顫顯顰"],
|
||||||
|
["f1a1","顱顴顳颪颯颱颶飄飃飆飩飫餃餉餒餔餘餡餝餞餤餠餬餮餽餾饂饉饅饐饋饑饒饌饕馗馘馥馭馮馼駟駛駝駘駑駭駮駱駲駻駸騁騏騅駢騙騫騷驅驂驀驃騾驕驍驛驗驟驢驥驤驩驫驪骭骰骼髀髏髑髓體髞髟髢髣髦髯髫髮髴髱髷"],
|
||||||
|
["f2a1","髻鬆鬘鬚鬟鬢鬣鬥鬧鬨鬩鬪鬮鬯鬲魄魃魏魍魎魑魘魴鮓鮃鮑鮖鮗鮟鮠鮨鮴鯀鯊鮹鯆鯏鯑鯒鯣鯢鯤鯔鯡鰺鯲鯱鯰鰕鰔鰉鰓鰌鰆鰈鰒鰊鰄鰮鰛鰥鰤鰡鰰鱇鰲鱆鰾鱚鱠鱧鱶鱸鳧鳬鳰鴉鴈鳫鴃鴆鴪鴦鶯鴣鴟鵄鴕鴒鵁鴿鴾鵆鵈"],
|
||||||
|
["f3a1","鵝鵞鵤鵑鵐鵙鵲鶉鶇鶫鵯鵺鶚鶤鶩鶲鷄鷁鶻鶸鶺鷆鷏鷂鷙鷓鷸鷦鷭鷯鷽鸚鸛鸞鹵鹹鹽麁麈麋麌麒麕麑麝麥麩麸麪麭靡黌黎黏黐黔黜點黝黠黥黨黯黴黶黷黹黻黼黽鼇鼈皷鼕鼡鼬鼾齊齒齔齣齟齠齡齦齧齬齪齷齲齶龕龜龠"],
|
||||||
|
["f4a1","堯槇遙瑤凜熙"],
|
||||||
|
["f9a1","纊褜鍈銈蓜俉炻昱棈鋹曻彅丨仡仼伀伃伹佖侒侊侚侔俍偀倢俿倞偆偰偂傔僴僘兊兤冝冾凬刕劜劦勀勛匀匇匤卲厓厲叝﨎咜咊咩哿喆坙坥垬埈埇﨏塚增墲夋奓奛奝奣妤妺孖寀甯寘寬尞岦岺峵崧嵓﨑嵂嵭嶸嶹巐弡弴彧德"],
|
||||||
|
["faa1","忞恝悅悊惞惕愠惲愑愷愰憘戓抦揵摠撝擎敎昀昕昻昉昮昞昤晥晗晙晴晳暙暠暲暿曺朎朗杦枻桒柀栁桄棏﨓楨﨔榘槢樰橫橆橳橾櫢櫤毖氿汜沆汯泚洄涇浯涖涬淏淸淲淼渹湜渧渼溿澈澵濵瀅瀇瀨炅炫焏焄煜煆煇凞燁燾犱"],
|
||||||
|
["fba1","犾猤猪獷玽珉珖珣珒琇珵琦琪琩琮瑢璉璟甁畯皂皜皞皛皦益睆劯砡硎硤硺礰礼神祥禔福禛竑竧靖竫箞精絈絜綷綠緖繒罇羡羽茁荢荿菇菶葈蒴蕓蕙蕫﨟薰蘒﨡蠇裵訒訷詹誧誾諟諸諶譓譿賰賴贒赶﨣軏﨤逸遧郞都鄕鄧釚"],
|
||||||
|
["fca1","釗釞釭釮釤釥鈆鈐鈊鈺鉀鈼鉎鉙鉑鈹鉧銧鉷鉸鋧鋗鋙鋐﨧鋕鋠鋓錥錡鋻﨨錞鋿錝錂鍰鍗鎤鏆鏞鏸鐱鑅鑈閒隆﨩隝隯霳霻靃靍靏靑靕顗顥飯飼餧館馞驎髙髜魵魲鮏鮱鮻鰀鵰鵫鶴鸙黑"],
|
||||||
|
["fcf1","ⅰ",9,"¬¦'""],
|
||||||
|
["8fa2af","˘ˇ¸˙˝¯˛˚~΄΅"],
|
||||||
|
["8fa2c2","¡¦¿"],
|
||||||
|
["8fa2eb","ºª©®™¤№"],
|
||||||
|
["8fa6e1","ΆΈΉΊΪ"],
|
||||||
|
["8fa6e7","Ό"],
|
||||||
|
["8fa6e9","ΎΫ"],
|
||||||
|
["8fa6ec","Ώ"],
|
||||||
|
["8fa6f1","άέήίϊΐόςύϋΰώ"],
|
||||||
|
["8fa7c2","Ђ",10,"ЎЏ"],
|
||||||
|
["8fa7f2","ђ",10,"ўџ"],
|
||||||
|
["8fa9a1","ÆĐ"],
|
||||||
|
["8fa9a4","Ħ"],
|
||||||
|
["8fa9a6","IJ"],
|
||||||
|
["8fa9a8","ŁĿ"],
|
||||||
|
["8fa9ab","ŊØŒ"],
|
||||||
|
["8fa9af","ŦÞ"],
|
||||||
|
["8fa9c1","æđðħıijĸłŀʼnŋøœßŧþ"],
|
||||||
|
["8faaa1","ÁÀÄÂĂǍĀĄÅÃĆĈČÇĊĎÉÈËÊĚĖĒĘ"],
|
||||||
|
["8faaba","ĜĞĢĠĤÍÌÏÎǏİĪĮĨĴĶĹĽĻŃŇŅÑÓÒÖÔǑŐŌÕŔŘŖŚŜŠŞŤŢÚÙÜÛŬǓŰŪŲŮŨǗǛǙǕŴÝŸŶŹŽŻ"],
|
||||||
|
["8faba1","áàäâăǎāąåãćĉčçċďéèëêěėēęǵĝğ"],
|
||||||
|
["8fabbd","ġĥíìïîǐ"],
|
||||||
|
["8fabc5","īįĩĵķĺľļńňņñóòöôǒőōõŕřŗśŝšşťţúùüûŭǔűūųůũǘǜǚǖŵýÿŷźžż"],
|
||||||
|
["8fb0a1","丂丄丅丌丒丟丣两丨丫丮丯丰丵乀乁乄乇乑乚乜乣乨乩乴乵乹乿亍亖亗亝亯亹仃仐仚仛仠仡仢仨仯仱仳仵份仾仿伀伂伃伈伋伌伒伕伖众伙伮伱你伳伵伷伹伻伾佀佂佈佉佋佌佒佔佖佘佟佣佪佬佮佱佷佸佹佺佽佾侁侂侄"],
|
||||||
|
["8fb1a1","侅侉侊侌侎侐侒侓侔侗侙侚侞侟侲侷侹侻侼侽侾俀俁俅俆俈俉俋俌俍俏俒俜俠俢俰俲俼俽俿倀倁倄倇倊倌倎倐倓倗倘倛倜倝倞倢倧倮倰倲倳倵偀偁偂偅偆偊偌偎偑偒偓偗偙偟偠偢偣偦偧偪偭偰偱倻傁傃傄傆傊傎傏傐"],
|
||||||
|
["8fb2a1","傒傓傔傖傛傜傞",4,"傪傯傰傹傺傽僀僃僄僇僌僎僐僓僔僘僜僝僟僢僤僦僨僩僯僱僶僺僾儃儆儇儈儋儌儍儎僲儐儗儙儛儜儝儞儣儧儨儬儭儯儱儳儴儵儸儹兂兊兏兓兕兗兘兟兤兦兾冃冄冋冎冘冝冡冣冭冸冺冼冾冿凂"],
|
||||||
|
["8fb3a1","凈减凑凒凓凕凘凞凢凥凮凲凳凴凷刁刂刅划刓刕刖刘刢刨刱刲刵刼剅剉剕剗剘剚剜剟剠剡剦剮剷剸剹劀劂劅劊劌劓劕劖劗劘劚劜劤劥劦劧劯劰劶劷劸劺劻劽勀勄勆勈勌勏勑勔勖勛勜勡勥勨勩勪勬勰勱勴勶勷匀匃匊匋"],
|
||||||
|
["8fb4a1","匌匑匓匘匛匜匞匟匥匧匨匩匫匬匭匰匲匵匼匽匾卂卌卋卙卛卡卣卥卬卭卲卹卾厃厇厈厎厓厔厙厝厡厤厪厫厯厲厴厵厷厸厺厽叀叅叏叒叓叕叚叝叞叠另叧叵吂吓吚吡吧吨吪启吱吴吵呃呄呇呍呏呞呢呤呦呧呩呫呭呮呴呿"],
|
||||||
|
["8fb5a1","咁咃咅咈咉咍咑咕咖咜咟咡咦咧咩咪咭咮咱咷咹咺咻咿哆哊响哎哠哪哬哯哶哼哾哿唀唁唅唈唉唌唍唎唕唪唫唲唵唶唻唼唽啁啇啉啊啍啐啑啘啚啛啞啠啡啤啦啿喁喂喆喈喎喏喑喒喓喔喗喣喤喭喲喿嗁嗃嗆嗉嗋嗌嗎嗑嗒"],
|
||||||
|
["8fb6a1","嗓嗗嗘嗛嗞嗢嗩嗶嗿嘅嘈嘊嘍",5,"嘙嘬嘰嘳嘵嘷嘹嘻嘼嘽嘿噀噁噃噄噆噉噋噍噏噔噞噠噡噢噣噦噩噭噯噱噲噵嚄嚅嚈嚋嚌嚕嚙嚚嚝嚞嚟嚦嚧嚨嚩嚫嚬嚭嚱嚳嚷嚾囅囉囊囋囏囐囌囍囙囜囝囟囡囤",4,"囱囫园"],
|
||||||
|
["8fb7a1","囶囷圁圂圇圊圌圑圕圚圛圝圠圢圣圤圥圩圪圬圮圯圳圴圽圾圿坅坆坌坍坒坢坥坧坨坫坭",4,"坳坴坵坷坹坺坻坼坾垁垃垌垔垗垙垚垜垝垞垟垡垕垧垨垩垬垸垽埇埈埌埏埕埝埞埤埦埧埩埭埰埵埶埸埽埾埿堃堄堈堉埡"],
|
||||||
|
["8fb8a1","堌堍堛堞堟堠堦堧堭堲堹堿塉塌塍塏塐塕塟塡塤塧塨塸塼塿墀墁墇墈墉墊墌墍墏墐墔墖墝墠墡墢墦墩墱墲壄墼壂壈壍壎壐壒壔壖壚壝壡壢壩壳夅夆夋夌夒夓夔虁夝夡夣夤夨夯夰夳夵夶夿奃奆奒奓奙奛奝奞奟奡奣奫奭"],
|
||||||
|
["8fb9a1","奯奲奵奶她奻奼妋妌妎妒妕妗妟妤妧妭妮妯妰妳妷妺妼姁姃姄姈姊姍姒姝姞姟姣姤姧姮姯姱姲姴姷娀娄娌娍娎娒娓娞娣娤娧娨娪娭娰婄婅婇婈婌婐婕婞婣婥婧婭婷婺婻婾媋媐媓媖媙媜媞媟媠媢媧媬媱媲媳媵媸媺媻媿"],
|
||||||
|
["8fbaa1","嫄嫆嫈嫏嫚嫜嫠嫥嫪嫮嫵嫶嫽嬀嬁嬈嬗嬴嬙嬛嬝嬡嬥嬭嬸孁孋孌孒孖孞孨孮孯孼孽孾孿宁宄宆宊宎宐宑宓宔宖宨宩宬宭宯宱宲宷宺宼寀寁寍寏寖",4,"寠寯寱寴寽尌尗尞尟尣尦尩尫尬尮尰尲尵尶屙屚屜屢屣屧屨屩"],
|
||||||
|
["8fbba1","屭屰屴屵屺屻屼屽岇岈岊岏岒岝岟岠岢岣岦岪岲岴岵岺峉峋峒峝峗峮峱峲峴崁崆崍崒崫崣崤崦崧崱崴崹崽崿嵂嵃嵆嵈嵕嵑嵙嵊嵟嵠嵡嵢嵤嵪嵭嵰嵹嵺嵾嵿嶁嶃嶈嶊嶒嶓嶔嶕嶙嶛嶟嶠嶧嶫嶰嶴嶸嶹巃巇巋巐巎巘巙巠巤"],
|
||||||
|
["8fbca1","巩巸巹帀帇帍帒帔帕帘帟帠帮帨帲帵帾幋幐幉幑幖幘幛幜幞幨幪",4,"幰庀庋庎庢庤庥庨庪庬庱庳庽庾庿廆廌廋廎廑廒廔廕廜廞廥廫异弆弇弈弎弙弜弝弡弢弣弤弨弫弬弮弰弴弶弻弽弿彀彄彅彇彍彐彔彘彛彠彣彤彧"],
|
||||||
|
["8fbda1","彯彲彴彵彸彺彽彾徉徍徏徖徜徝徢徧徫徤徬徯徰徱徸忄忇忈忉忋忐",4,"忞忡忢忨忩忪忬忭忮忯忲忳忶忺忼怇怊怍怓怔怗怘怚怟怤怭怳怵恀恇恈恉恌恑恔恖恗恝恡恧恱恾恿悂悆悈悊悎悑悓悕悘悝悞悢悤悥您悰悱悷"],
|
||||||
|
["8fbea1","悻悾惂惄惈惉惊惋惎惏惔惕惙惛惝惞惢惥惲惵惸惼惽愂愇愊愌愐",4,"愖愗愙愜愞愢愪愫愰愱愵愶愷愹慁慅慆慉慞慠慬慲慸慻慼慿憀憁憃憄憋憍憒憓憗憘憜憝憟憠憥憨憪憭憸憹憼懀懁懂懎懏懕懜懝懞懟懡懢懧懩懥"],
|
||||||
|
["8fbfa1","懬懭懯戁戃戄戇戓戕戜戠戢戣戧戩戫戹戽扂扃扄扆扌扐扑扒扔扖扚扜扤扭扯扳扺扽抍抎抏抐抦抨抳抶抷抺抾抿拄拎拕拖拚拪拲拴拼拽挃挄挊挋挍挐挓挖挘挩挪挭挵挶挹挼捁捂捃捄捆捊捋捎捒捓捔捘捛捥捦捬捭捱捴捵"],
|
||||||
|
["8fc0a1","捸捼捽捿掂掄掇掊掐掔掕掙掚掞掤掦掭掮掯掽揁揅揈揎揑揓揔揕揜揠揥揪揬揲揳揵揸揹搉搊搐搒搔搘搞搠搢搤搥搩搪搯搰搵搽搿摋摏摑摒摓摔摚摛摜摝摟摠摡摣摭摳摴摻摽撅撇撏撐撑撘撙撛撝撟撡撣撦撨撬撳撽撾撿"],
|
||||||
|
["8fc1a1","擄擉擊擋擌擎擐擑擕擗擤擥擩擪擭擰擵擷擻擿攁攄攈攉攊攏攓攔攖攙攛攞攟攢攦攩攮攱攺攼攽敃敇敉敐敒敔敟敠敧敫敺敽斁斅斊斒斕斘斝斠斣斦斮斲斳斴斿旂旈旉旎旐旔旖旘旟旰旲旴旵旹旾旿昀昄昈昉昍昑昒昕昖昝"],
|
||||||
|
["8fc2a1","昞昡昢昣昤昦昩昪昫昬昮昰昱昳昹昷晀晅晆晊晌晑晎晗晘晙晛晜晠晡曻晪晫晬晾晳晵晿晷晸晹晻暀晼暋暌暍暐暒暙暚暛暜暟暠暤暭暱暲暵暻暿曀曂曃曈曌曎曏曔曛曟曨曫曬曮曺朅朇朎朓朙朜朠朢朳朾杅杇杈杌杔杕杝"],
|
||||||
|
["8fc3a1","杦杬杮杴杶杻极构枎枏枑枓枖枘枙枛枰枱枲枵枻枼枽柹柀柂柃柅柈柉柒柗柙柜柡柦柰柲柶柷桒栔栙栝栟栨栧栬栭栯栰栱栳栻栿桄桅桊桌桕桗桘桛桫桮",4,"桵桹桺桻桼梂梄梆梈梖梘梚梜梡梣梥梩梪梮梲梻棅棈棌棏"],
|
||||||
|
["8fc4a1","棐棑棓棖棙棜棝棥棨棪棫棬棭棰棱棵棶棻棼棽椆椉椊椐椑椓椖椗椱椳椵椸椻楂楅楉楎楗楛楣楤楥楦楨楩楬楰楱楲楺楻楿榀榍榒榖榘榡榥榦榨榫榭榯榷榸榺榼槅槈槑槖槗槢槥槮槯槱槳槵槾樀樁樃樏樑樕樚樝樠樤樨樰樲"],
|
||||||
|
["8fc5a1","樴樷樻樾樿橅橆橉橊橎橐橑橒橕橖橛橤橧橪橱橳橾檁檃檆檇檉檋檑檛檝檞檟檥檫檯檰檱檴檽檾檿櫆櫉櫈櫌櫐櫔櫕櫖櫜櫝櫤櫧櫬櫰櫱櫲櫼櫽欂欃欆欇欉欏欐欑欗欛欞欤欨欫欬欯欵欶欻欿歆歊歍歒歖歘歝歠歧歫歮歰歵歽"],
|
||||||
|
["8fc6a1","歾殂殅殗殛殟殠殢殣殨殩殬殭殮殰殸殹殽殾毃毄毉毌毖毚毡毣毦毧毮毱毷毹毿氂氄氅氉氍氎氐氒氙氟氦氧氨氬氮氳氵氶氺氻氿汊汋汍汏汒汔汙汛汜汫汭汯汴汶汸汹汻沅沆沇沉沔沕沗沘沜沟沰沲沴泂泆泍泏泐泑泒泔泖"],
|
||||||
|
["8fc7a1","泚泜泠泧泩泫泬泮泲泴洄洇洊洎洏洑洓洚洦洧洨汧洮洯洱洹洼洿浗浞浟浡浥浧浯浰浼涂涇涑涒涔涖涗涘涪涬涴涷涹涽涿淄淈淊淎淏淖淛淝淟淠淢淥淩淯淰淴淶淼渀渄渞渢渧渲渶渹渻渼湄湅湈湉湋湏湑湒湓湔湗湜湝湞"],
|
||||||
|
["8fc8a1","湢湣湨湳湻湽溍溓溙溠溧溭溮溱溳溻溿滀滁滃滇滈滊滍滎滏滫滭滮滹滻滽漄漈漊漌漍漖漘漚漛漦漩漪漯漰漳漶漻漼漭潏潑潒潓潗潙潚潝潞潡潢潨潬潽潾澃澇澈澋澌澍澐澒澓澔澖澚澟澠澥澦澧澨澮澯澰澵澶澼濅濇濈濊"],
|
||||||
|
["8fc9a1","濚濞濨濩濰濵濹濼濽瀀瀅瀆瀇瀍瀗瀠瀣瀯瀴瀷瀹瀼灃灄灈灉灊灋灔灕灝灞灎灤灥灬灮灵灶灾炁炅炆炔",4,"炛炤炫炰炱炴炷烊烑烓烔烕烖烘烜烤烺焃",4,"焋焌焏焞焠焫焭焯焰焱焸煁煅煆煇煊煋煐煒煗煚煜煞煠"],
|
||||||
|
["8fcaa1","煨煹熀熅熇熌熒熚熛熠熢熯熰熲熳熺熿燀燁燄燋燌燓燖燙燚燜燸燾爀爇爈爉爓爗爚爝爟爤爫爯爴爸爹牁牂牃牅牎牏牐牓牕牖牚牜牞牠牣牨牫牮牯牱牷牸牻牼牿犄犉犍犎犓犛犨犭犮犱犴犾狁狇狉狌狕狖狘狟狥狳狴狺狻"],
|
||||||
|
["8fcba1","狾猂猄猅猇猋猍猒猓猘猙猞猢猤猧猨猬猱猲猵猺猻猽獃獍獐獒獖獘獝獞獟獠獦獧獩獫獬獮獯獱獷獹獼玀玁玃玅玆玎玐玓玕玗玘玜玞玟玠玢玥玦玪玫玭玵玷玹玼玽玿珅珆珉珋珌珏珒珓珖珙珝珡珣珦珧珩珴珵珷珹珺珻珽"],
|
||||||
|
["8fcca1","珿琀琁琄琇琊琑琚琛琤琦琨",9,"琹瑀瑃瑄瑆瑇瑋瑍瑑瑒瑗瑝瑢瑦瑧瑨瑫瑭瑮瑱瑲璀璁璅璆璇璉璏璐璑璒璘璙璚璜璟璠璡璣璦璨璩璪璫璮璯璱璲璵璹璻璿瓈瓉瓌瓐瓓瓘瓚瓛瓞瓟瓤瓨瓪瓫瓯瓴瓺瓻瓼瓿甆"],
|
||||||
|
["8fcda1","甒甖甗甠甡甤甧甩甪甯甶甹甽甾甿畀畃畇畈畎畐畒畗畞畟畡畯畱畹",5,"疁疅疐疒疓疕疙疜疢疤疴疺疿痀痁痄痆痌痎痏痗痜痟痠痡痤痧痬痮痯痱痹瘀瘂瘃瘄瘇瘈瘊瘌瘏瘒瘓瘕瘖瘙瘛瘜瘝瘞瘣瘥瘦瘩瘭瘲瘳瘵瘸瘹"],
|
||||||
|
["8fcea1","瘺瘼癊癀癁癃癄癅癉癋癕癙癟癤癥癭癮癯癱癴皁皅皌皍皕皛皜皝皟皠皢",6,"皪皭皽盁盅盉盋盌盎盔盙盠盦盨盬盰盱盶盹盼眀眆眊眎眒眔眕眗眙眚眜眢眨眭眮眯眴眵眶眹眽眾睂睅睆睊睍睎睏睒睖睗睜睞睟睠睢"],
|
||||||
|
["8fcfa1","睤睧睪睬睰睲睳睴睺睽瞀瞄瞌瞍瞔瞕瞖瞚瞟瞢瞧瞪瞮瞯瞱瞵瞾矃矉矑矒矕矙矞矟矠矤矦矪矬矰矱矴矸矻砅砆砉砍砎砑砝砡砢砣砭砮砰砵砷硃硄硇硈硌硎硒硜硞硠硡硣硤硨硪确硺硾碊碏碔碘碡碝碞碟碤碨碬碭碰碱碲碳"],
|
||||||
|
["8fd0a1","碻碽碿磇磈磉磌磎磒磓磕磖磤磛磟磠磡磦磪磲磳礀磶磷磺磻磿礆礌礐礚礜礞礟礠礥礧礩礭礱礴礵礻礽礿祄祅祆祊祋祏祑祔祘祛祜祧祩祫祲祹祻祼祾禋禌禑禓禔禕禖禘禛禜禡禨禩禫禯禱禴禸离秂秄秇秈秊秏秔秖秚秝秞"],
|
||||||
|
["8fd1a1","秠秢秥秪秫秭秱秸秼稂稃稇稉稊稌稑稕稛稞稡稧稫稭稯稰稴稵稸稹稺穄穅穇穈穌穕穖穙穜穝穟穠穥穧穪穭穵穸穾窀窂窅窆窊窋窐窑窔窞窠窣窬窳窵窹窻窼竆竉竌竎竑竛竨竩竫竬竱竴竻竽竾笇笔笟笣笧笩笪笫笭笮笯笰"],
|
||||||
|
["8fd2a1","笱笴笽笿筀筁筇筎筕筠筤筦筩筪筭筯筲筳筷箄箉箎箐箑箖箛箞箠箥箬箯箰箲箵箶箺箻箼箽篂篅篈篊篔篖篗篙篚篛篨篪篲篴篵篸篹篺篼篾簁簂簃簄簆簉簋簌簎簏簙簛簠簥簦簨簬簱簳簴簶簹簺籆籊籕籑籒籓籙",5],
|
||||||
|
["8fd3a1","籡籣籧籩籭籮籰籲籹籼籽粆粇粏粔粞粠粦粰粶粷粺粻粼粿糄糇糈糉糍糏糓糔糕糗糙糚糝糦糩糫糵紃紇紈紉紏紑紒紓紖紝紞紣紦紪紭紱紼紽紾絀絁絇絈絍絑絓絗絙絚絜絝絥絧絪絰絸絺絻絿綁綂綃綅綆綈綋綌綍綑綖綗綝"],
|
||||||
|
["8fd4a1","綞綦綧綪綳綶綷綹緂",4,"緌緍緎緗緙縀緢緥緦緪緫緭緱緵緶緹緺縈縐縑縕縗縜縝縠縧縨縬縭縯縳縶縿繄繅繇繎繐繒繘繟繡繢繥繫繮繯繳繸繾纁纆纇纊纍纑纕纘纚纝纞缼缻缽缾缿罃罄罇罏罒罓罛罜罝罡罣罤罥罦罭"],
|
||||||
|
["8fd5a1","罱罽罾罿羀羋羍羏羐羑羖羗羜羡羢羦羪羭羴羼羿翀翃翈翎翏翛翟翣翥翨翬翮翯翲翺翽翾翿耇耈耊耍耎耏耑耓耔耖耝耞耟耠耤耦耬耮耰耴耵耷耹耺耼耾聀聄聠聤聦聭聱聵肁肈肎肜肞肦肧肫肸肹胈胍胏胒胔胕胗胘胠胭胮"],
|
||||||
|
["8fd6a1","胰胲胳胶胹胺胾脃脋脖脗脘脜脞脠脤脧脬脰脵脺脼腅腇腊腌腒腗腠腡腧腨腩腭腯腷膁膐膄膅膆膋膎膖膘膛膞膢膮膲膴膻臋臃臅臊臎臏臕臗臛臝臞臡臤臫臬臰臱臲臵臶臸臹臽臿舀舃舏舓舔舙舚舝舡舢舨舲舴舺艃艄艅艆"],
|
||||||
|
["8fd7a1","艋艎艏艑艖艜艠艣艧艭艴艻艽艿芀芁芃芄芇芉芊芎芑芔芖芘芚芛芠芡芣芤芧芨芩芪芮芰芲芴芷芺芼芾芿苆苐苕苚苠苢苤苨苪苭苯苶苷苽苾茀茁茇茈茊茋荔茛茝茞茟茡茢茬茭茮茰茳茷茺茼茽荂荃荄荇荍荎荑荕荖荗荰荸"],
|
||||||
|
["8fd8a1","荽荿莀莂莄莆莍莒莔莕莘莙莛莜莝莦莧莩莬莾莿菀菇菉菏菐菑菔菝荓菨菪菶菸菹菼萁萆萊萏萑萕萙莭萯萹葅葇葈葊葍葏葑葒葖葘葙葚葜葠葤葥葧葪葰葳葴葶葸葼葽蒁蒅蒒蒓蒕蒞蒦蒨蒩蒪蒯蒱蒴蒺蒽蒾蓀蓂蓇蓈蓌蓏蓓"],
|
||||||
|
["8fd9a1","蓜蓧蓪蓯蓰蓱蓲蓷蔲蓺蓻蓽蔂蔃蔇蔌蔎蔐蔜蔞蔢蔣蔤蔥蔧蔪蔫蔯蔳蔴蔶蔿蕆蕏",4,"蕖蕙蕜",6,"蕤蕫蕯蕹蕺蕻蕽蕿薁薅薆薉薋薌薏薓薘薝薟薠薢薥薧薴薶薷薸薼薽薾薿藂藇藊藋藎薭藘藚藟藠藦藨藭藳藶藼"],
|
||||||
|
["8fdaa1","藿蘀蘄蘅蘍蘎蘐蘑蘒蘘蘙蘛蘞蘡蘧蘩蘶蘸蘺蘼蘽虀虂虆虒虓虖虗虘虙虝虠",4,"虩虬虯虵虶虷虺蚍蚑蚖蚘蚚蚜蚡蚦蚧蚨蚭蚱蚳蚴蚵蚷蚸蚹蚿蛀蛁蛃蛅蛑蛒蛕蛗蛚蛜蛠蛣蛥蛧蚈蛺蛼蛽蜄蜅蜇蜋蜎蜏蜐蜓蜔蜙蜞蜟蜡蜣"],
|
||||||
|
["8fdba1","蜨蜮蜯蜱蜲蜹蜺蜼蜽蜾蝀蝃蝅蝍蝘蝝蝡蝤蝥蝯蝱蝲蝻螃",6,"螋螌螐螓螕螗螘螙螞螠螣螧螬螭螮螱螵螾螿蟁蟈蟉蟊蟎蟕蟖蟙蟚蟜蟟蟢蟣蟤蟪蟫蟭蟱蟳蟸蟺蟿蠁蠃蠆蠉蠊蠋蠐蠙蠒蠓蠔蠘蠚蠛蠜蠞蠟蠨蠭蠮蠰蠲蠵"],
|
||||||
|
["8fdca1","蠺蠼衁衃衅衈衉衊衋衎衑衕衖衘衚衜衟衠衤衩衱衹衻袀袘袚袛袜袟袠袨袪袺袽袾裀裊",4,"裑裒裓裛裞裧裯裰裱裵裷褁褆褍褎褏褕褖褘褙褚褜褠褦褧褨褰褱褲褵褹褺褾襀襂襅襆襉襏襒襗襚襛襜襡襢襣襫襮襰襳襵襺"],
|
||||||
|
["8fdda1","襻襼襽覉覍覐覔覕覛覜覟覠覥覰覴覵覶覷覼觔",4,"觥觩觫觭觱觳觶觹觽觿訄訅訇訏訑訒訔訕訞訠訢訤訦訫訬訯訵訷訽訾詀詃詅詇詉詍詎詓詖詗詘詜詝詡詥詧詵詶詷詹詺詻詾詿誀誃誆誋誏誐誒誖誗誙誟誧誩誮誯誳"],
|
||||||
|
["8fdea1","誶誷誻誾諃諆諈諉諊諑諓諔諕諗諝諟諬諰諴諵諶諼諿謅謆謋謑謜謞謟謊謭謰謷謼譂",4,"譈譒譓譔譙譍譞譣譭譶譸譹譼譾讁讄讅讋讍讏讔讕讜讞讟谸谹谽谾豅豇豉豋豏豑豓豔豗豘豛豝豙豣豤豦豨豩豭豳豵豶豻豾貆"],
|
||||||
|
["8fdfa1","貇貋貐貒貓貙貛貜貤貹貺賅賆賉賋賏賖賕賙賝賡賨賬賯賰賲賵賷賸賾賿贁贃贉贒贗贛赥赩赬赮赿趂趄趈趍趐趑趕趞趟趠趦趫趬趯趲趵趷趹趻跀跅跆跇跈跊跎跑跔跕跗跙跤跥跧跬跰趼跱跲跴跽踁踄踅踆踋踑踔踖踠踡踢"],
|
||||||
|
["8fe0a1","踣踦踧踱踳踶踷踸踹踽蹀蹁蹋蹍蹎蹏蹔蹛蹜蹝蹞蹡蹢蹩蹬蹭蹯蹰蹱蹹蹺蹻躂躃躉躐躒躕躚躛躝躞躢躧躩躭躮躳躵躺躻軀軁軃軄軇軏軑軔軜軨軮軰軱軷軹軺軭輀輂輇輈輏輐輖輗輘輞輠輡輣輥輧輨輬輭輮輴輵輶輷輺轀轁"],
|
||||||
|
["8fe1a1","轃轇轏轑",4,"轘轝轞轥辝辠辡辤辥辦辵辶辸达迀迁迆迊迋迍运迒迓迕迠迣迤迨迮迱迵迶迻迾适逄逈逌逘逛逨逩逯逪逬逭逳逴逷逿遃遄遌遛遝遢遦遧遬遰遴遹邅邈邋邌邎邐邕邗邘邙邛邠邡邢邥邰邲邳邴邶邽郌邾郃"],
|
||||||
|
["8fe2a1","郄郅郇郈郕郗郘郙郜郝郟郥郒郶郫郯郰郴郾郿鄀鄄鄅鄆鄈鄍鄐鄔鄖鄗鄘鄚鄜鄞鄠鄥鄢鄣鄧鄩鄮鄯鄱鄴鄶鄷鄹鄺鄼鄽酃酇酈酏酓酗酙酚酛酡酤酧酭酴酹酺酻醁醃醅醆醊醎醑醓醔醕醘醞醡醦醨醬醭醮醰醱醲醳醶醻醼醽醿"],
|
||||||
|
["8fe3a1","釂釃釅釓釔釗釙釚釞釤釥釩釪釬",5,"釷釹釻釽鈀鈁鈄鈅鈆鈇鈉鈊鈌鈐鈒鈓鈖鈘鈜鈝鈣鈤鈥鈦鈨鈮鈯鈰鈳鈵鈶鈸鈹鈺鈼鈾鉀鉂鉃鉆鉇鉊鉍鉎鉏鉑鉘鉙鉜鉝鉠鉡鉥鉧鉨鉩鉮鉯鉰鉵",4,"鉻鉼鉽鉿銈銉銊銍銎銒銗"],
|
||||||
|
["8fe4a1","銙銟銠銤銥銧銨銫銯銲銶銸銺銻銼銽銿",4,"鋅鋆鋇鋈鋋鋌鋍鋎鋐鋓鋕鋗鋘鋙鋜鋝鋟鋠鋡鋣鋥鋧鋨鋬鋮鋰鋹鋻鋿錀錂錈錍錑錔錕錜錝錞錟錡錤錥錧錩錪錳錴錶錷鍇鍈鍉鍐鍑鍒鍕鍗鍘鍚鍞鍤鍥鍧鍩鍪鍭鍯鍰鍱鍳鍴鍶"],
|
||||||
|
["8fe5a1","鍺鍽鍿鎀鎁鎂鎈鎊鎋鎍鎏鎒鎕鎘鎛鎞鎡鎣鎤鎦鎨鎫鎴鎵鎶鎺鎩鏁鏄鏅鏆鏇鏉",4,"鏓鏙鏜鏞鏟鏢鏦鏧鏹鏷鏸鏺鏻鏽鐁鐂鐄鐈鐉鐍鐎鐏鐕鐖鐗鐟鐮鐯鐱鐲鐳鐴鐻鐿鐽鑃鑅鑈鑊鑌鑕鑙鑜鑟鑡鑣鑨鑫鑭鑮鑯鑱鑲钄钃镸镹"],
|
||||||
|
["8fe6a1","镾閄閈閌閍閎閝閞閟閡閦閩閫閬閴閶閺閽閿闆闈闉闋闐闑闒闓闙闚闝闞闟闠闤闦阝阞阢阤阥阦阬阱阳阷阸阹阺阼阽陁陒陔陖陗陘陡陮陴陻陼陾陿隁隂隃隄隉隑隖隚隝隟隤隥隦隩隮隯隳隺雊雒嶲雘雚雝雞雟雩雯雱雺霂"],
|
||||||
|
["8fe7a1","霃霅霉霚霛霝霡霢霣霨霱霳靁靃靊靎靏靕靗靘靚靛靣靧靪靮靳靶靷靸靻靽靿鞀鞉鞕鞖鞗鞙鞚鞞鞟鞢鞬鞮鞱鞲鞵鞶鞸鞹鞺鞼鞾鞿韁韄韅韇韉韊韌韍韎韐韑韔韗韘韙韝韞韠韛韡韤韯韱韴韷韸韺頇頊頙頍頎頔頖頜頞頠頣頦"],
|
||||||
|
["8fe8a1","頫頮頯頰頲頳頵頥頾顄顇顊顑顒顓顖顗顙顚顢顣顥顦顪顬颫颭颮颰颴颷颸颺颻颿飂飅飈飌飡飣飥飦飧飪飳飶餂餇餈餑餕餖餗餚餛餜餟餢餦餧餫餱",4,"餹餺餻餼饀饁饆饇饈饍饎饔饘饙饛饜饞饟饠馛馝馟馦馰馱馲馵"],
|
||||||
|
["8fe9a1","馹馺馽馿駃駉駓駔駙駚駜駞駧駪駫駬駰駴駵駹駽駾騂騃騄騋騌騐騑騖騞騠騢騣騤騧騭騮騳騵騶騸驇驁驄驊驋驌驎驑驔驖驝骪骬骮骯骲骴骵骶骹骻骾骿髁髃髆髈髎髐髒髕髖髗髛髜髠髤髥髧髩髬髲髳髵髹髺髽髿",4],
|
||||||
|
["8feaa1","鬄鬅鬈鬉鬋鬌鬍鬎鬐鬒鬖鬙鬛鬜鬠鬦鬫鬭鬳鬴鬵鬷鬹鬺鬽魈魋魌魕魖魗魛魞魡魣魥魦魨魪",4,"魳魵魷魸魹魿鮀鮄鮅鮆鮇鮉鮊鮋鮍鮏鮐鮔鮚鮝鮞鮦鮧鮩鮬鮰鮱鮲鮷鮸鮻鮼鮾鮿鯁鯇鯈鯎鯐鯗鯘鯝鯟鯥鯧鯪鯫鯯鯳鯷鯸"],
|
||||||
|
["8feba1","鯹鯺鯽鯿鰀鰂鰋鰏鰑鰖鰘鰙鰚鰜鰞鰢鰣鰦",4,"鰱鰵鰶鰷鰽鱁鱃鱄鱅鱉鱊鱎鱏鱐鱓鱔鱖鱘鱛鱝鱞鱟鱣鱩鱪鱜鱫鱨鱮鱰鱲鱵鱷鱻鳦鳲鳷鳹鴋鴂鴑鴗鴘鴜鴝鴞鴯鴰鴲鴳鴴鴺鴼鵅鴽鵂鵃鵇鵊鵓鵔鵟鵣鵢鵥鵩鵪鵫鵰鵶鵷鵻"],
|
||||||
|
["8feca1","鵼鵾鶃鶄鶆鶊鶍鶎鶒鶓鶕鶖鶗鶘鶡鶪鶬鶮鶱鶵鶹鶼鶿鷃鷇鷉鷊鷔鷕鷖鷗鷚鷞鷟鷠鷥鷧鷩鷫鷮鷰鷳鷴鷾鸊鸂鸇鸎鸐鸑鸒鸕鸖鸙鸜鸝鹺鹻鹼麀麂麃麄麅麇麎麏麖麘麛麞麤麨麬麮麯麰麳麴麵黆黈黋黕黟黤黧黬黭黮黰黱黲黵"],
|
||||||
|
["8feda1","黸黿鼂鼃鼉鼏鼐鼑鼒鼔鼖鼗鼙鼚鼛鼟鼢鼦鼪鼫鼯鼱鼲鼴鼷鼹鼺鼼鼽鼿齁齃",4,"齓齕齖齗齘齚齝齞齨齩齭",4,"齳齵齺齽龏龐龑龒龔龖龗龞龡龢龣龥"]
|
||||||
|
]
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
# call-bound <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
|
||||||
|
|
||||||
|
[![github actions][actions-image]][actions-url]
|
||||||
|
[![coverage][codecov-image]][codecov-url]
|
||||||
|
[![dependency status][deps-svg]][deps-url]
|
||||||
|
[![dev dependency status][dev-deps-svg]][dev-deps-url]
|
||||||
|
[![License][license-image]][license-url]
|
||||||
|
[![Downloads][downloads-image]][downloads-url]
|
||||||
|
|
||||||
|
[![npm badge][npm-badge-png]][package-url]
|
||||||
|
|
||||||
|
Robust call-bound JavaScript intrinsics, using `call-bind` and `get-intrinsic`.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save call-bound
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage/Examples
|
||||||
|
|
||||||
|
```js
|
||||||
|
const assert = require('assert');
|
||||||
|
const callBound = require('call-bound');
|
||||||
|
|
||||||
|
const slice = callBound('Array.prototype.slice');
|
||||||
|
|
||||||
|
delete Function.prototype.call;
|
||||||
|
delete Function.prototype.bind;
|
||||||
|
delete Array.prototype.slice;
|
||||||
|
|
||||||
|
assert.deepEqual(slice([1, 2, 3, 4], 1, -1), [2, 3]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Clone the repo, `npm install`, and run `npm test`
|
||||||
|
|
||||||
|
[package-url]: https://npmjs.org/package/call-bound
|
||||||
|
[npm-version-svg]: https://versionbadg.es/ljharb/call-bound.svg
|
||||||
|
[deps-svg]: https://david-dm.org/ljharb/call-bound.svg
|
||||||
|
[deps-url]: https://david-dm.org/ljharb/call-bound
|
||||||
|
[dev-deps-svg]: https://david-dm.org/ljharb/call-bound/dev-status.svg
|
||||||
|
[dev-deps-url]: https://david-dm.org/ljharb/call-bound#info=devDependencies
|
||||||
|
[npm-badge-png]: https://nodei.co/npm/call-bound.png?downloads=true&stars=true
|
||||||
|
[license-image]: https://img.shields.io/npm/l/call-bound.svg
|
||||||
|
[license-url]: LICENSE
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/call-bound.svg
|
||||||
|
[downloads-url]: https://npm-stat.com/charts.html?package=call-bound
|
||||||
|
[codecov-image]: https://codecov.io/gh/ljharb/call-bound/branch/main/graphs/badge.svg
|
||||||
|
[codecov-url]: https://app.codecov.io/gh/ljharb/call-bound/
|
||||||
|
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/call-bound
|
||||||
|
[actions-url]: https://github.com/ljharb/call-bound/actions
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const win32 = process.platform === 'win32';
|
||||||
|
const {
|
||||||
|
REGEX_BACKSLASH,
|
||||||
|
REGEX_REMOVE_BACKSLASH,
|
||||||
|
REGEX_SPECIAL_CHARS,
|
||||||
|
REGEX_SPECIAL_CHARS_GLOBAL
|
||||||
|
} = require('./constants');
|
||||||
|
|
||||||
|
exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
|
||||||
|
exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str);
|
||||||
|
exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str);
|
||||||
|
exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1');
|
||||||
|
exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/');
|
||||||
|
|
||||||
|
exports.removeBackslashes = str => {
|
||||||
|
return str.replace(REGEX_REMOVE_BACKSLASH, match => {
|
||||||
|
return match === '\\' ? '' : match;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.supportsLookbehinds = () => {
|
||||||
|
const segs = process.version.slice(1).split('.').map(Number);
|
||||||
|
if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.isWindows = options => {
|
||||||
|
if (options && typeof options.windows === 'boolean') {
|
||||||
|
return options.windows;
|
||||||
|
}
|
||||||
|
return win32 === true || path.sep === '\\';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.escapeLast = (input, char, lastIdx) => {
|
||||||
|
const idx = input.lastIndexOf(char, lastIdx);
|
||||||
|
if (idx === -1) return input;
|
||||||
|
if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1);
|
||||||
|
return `${input.slice(0, idx)}\\${input.slice(idx)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.removePrefix = (input, state = {}) => {
|
||||||
|
let output = input;
|
||||||
|
if (output.startsWith('./')) {
|
||||||
|
output = output.slice(2);
|
||||||
|
state.prefix = './';
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.wrapOutput = (input, state = {}, options = {}) => {
|
||||||
|
const prepend = options.contains ? '' : '^';
|
||||||
|
const append = options.contains ? '' : '$';
|
||||||
|
|
||||||
|
let output = `${prepend}(?:${input})${append}`;
|
||||||
|
if (state.negated === true) {
|
||||||
|
output = `(?:^(?!${output}).*$)`;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
|
||||||
|
"extends": "@ljharb",
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
"id-length": 0,
|
||||||
|
"max-lines-per-function": 0,
|
||||||
|
"multiline-comment-style": 1,
|
||||||
|
"new-cap": [2, { "capIsNewExceptions": ["GetIntrinsic"] }],
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var utils = require('../utils');
|
||||||
|
|
||||||
|
// internal
|
||||||
|
var reEscComments = /\\#/g;
|
||||||
|
// note that '^^' is used in place of escaped comments
|
||||||
|
var reUnescapeComments = /\^\^/g;
|
||||||
|
var reComments = /#.*$/;
|
||||||
|
var reEscapeChars = /[.|\-[\]()\\]/g;
|
||||||
|
var reAsterisk = /\*/g;
|
||||||
|
|
||||||
|
module.exports = add;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts file patterns or regular expressions to nodemon
|
||||||
|
* compatible RegExp matching rules. Note: the `rules` argument
|
||||||
|
* object is modified to include the new rule and new RegExp
|
||||||
|
*
|
||||||
|
* ### Example:
|
||||||
|
*
|
||||||
|
* var rules = { watch: [], ignore: [] };
|
||||||
|
* add(rules, 'watch', '*.js');
|
||||||
|
* add(rules, 'ignore', '/public/');
|
||||||
|
* add(rules, 'watch', ':(\d)*\.js'); // note: string based regexp
|
||||||
|
* add(rules, 'watch', /\d*\.js/);
|
||||||
|
*
|
||||||
|
* @param {Object} rules containing `watch` and `ignore`. Also updated during
|
||||||
|
* execution
|
||||||
|
* @param {String} which must be either "watch" or "ignore"
|
||||||
|
* @param {String|RegExp} rule the actual rule.
|
||||||
|
*/
|
||||||
|
function add(rules, which, rule) {
|
||||||
|
if (!{ ignore: 1, watch: 1}[which]) {
|
||||||
|
throw new Error('rules/index.js#add requires "ignore" or "watch" as the ' +
|
||||||
|
'first argument');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(rule)) {
|
||||||
|
rule.forEach(function (rule) {
|
||||||
|
add(rules, which, rule);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// support the rule being a RegExp, but reformat it to
|
||||||
|
// the custom :<regexp> format that we're working with.
|
||||||
|
if (rule instanceof RegExp) {
|
||||||
|
// rule = ':' + rule.toString().replace(/^\/(.*?)\/$/g, '$1');
|
||||||
|
utils.log.error('RegExp format no longer supported, but globs are.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove comments and trim lines
|
||||||
|
// this mess of replace methods is escaping "\#" to allow for emacs temp files
|
||||||
|
|
||||||
|
// first up strip comments and remove blank head or tails
|
||||||
|
rule = (rule || '').replace(reEscComments, '^^')
|
||||||
|
.replace(reComments, '')
|
||||||
|
.replace(reUnescapeComments, '#').trim();
|
||||||
|
|
||||||
|
var regexp = false;
|
||||||
|
|
||||||
|
if (typeof rule === 'string' && rule.substring(0, 1) === ':') {
|
||||||
|
rule = rule.substring(1);
|
||||||
|
utils.log.error('RegExp no longer supported: ' + rule);
|
||||||
|
regexp = true;
|
||||||
|
} else if (rule.length === 0) {
|
||||||
|
// blank line (or it was a comment)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regexp) {
|
||||||
|
// rules[which].push(rule);
|
||||||
|
} else {
|
||||||
|
// rule = rule.replace(reEscapeChars, '\\$&')
|
||||||
|
// .replace(reAsterisk, '.*');
|
||||||
|
|
||||||
|
rules[which].push(rule);
|
||||||
|
// compile a regexp of all the rules for this ignore or watch
|
||||||
|
var re = rules[which].map(function (rule) {
|
||||||
|
return rule.replace(reEscapeChars, '\\$&')
|
||||||
|
.replace(reAsterisk, '.*');
|
||||||
|
}).join('|');
|
||||||
|
|
||||||
|
// used for the directory matching
|
||||||
|
rules[which].re = new RegExp(re);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,491 @@
|
|||||||
|
# body-parser
|
||||||
|
|
||||||
|
[![NPM Version][npm-version-image]][npm-url]
|
||||||
|
[![NPM Downloads][npm-downloads-image]][npm-url]
|
||||||
|
[![Build Status][ci-image]][ci-url]
|
||||||
|
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||||
|
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
|
||||||
|
|
||||||
|
Node.js body parsing middleware.
|
||||||
|
|
||||||
|
Parse incoming request bodies in a middleware before your handlers, available
|
||||||
|
under the `req.body` property.
|
||||||
|
|
||||||
|
**Note** As `req.body`'s shape is based on user-controlled input, all
|
||||||
|
properties and values in this object are untrusted and should be validated
|
||||||
|
before trusting. For example, `req.body.foo.toString()` may fail in multiple
|
||||||
|
ways, for example the `foo` property may not be there or may not be a string,
|
||||||
|
and `toString` may not be a function and instead a string or other user input.
|
||||||
|
|
||||||
|
[Learn about the anatomy of an HTTP transaction in Node.js](https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/).
|
||||||
|
|
||||||
|
_This does not handle multipart bodies_, due to their complex and typically
|
||||||
|
large nature. For multipart bodies, you may be interested in the following
|
||||||
|
modules:
|
||||||
|
|
||||||
|
* [busboy](https://www.npmjs.org/package/busboy#readme) and
|
||||||
|
[connect-busboy](https://www.npmjs.org/package/connect-busboy#readme)
|
||||||
|
* [multiparty](https://www.npmjs.org/package/multiparty#readme) and
|
||||||
|
[connect-multiparty](https://www.npmjs.org/package/connect-multiparty#readme)
|
||||||
|
* [formidable](https://www.npmjs.org/package/formidable#readme)
|
||||||
|
* [multer](https://www.npmjs.org/package/multer#readme)
|
||||||
|
|
||||||
|
This module provides the following parsers:
|
||||||
|
|
||||||
|
* [JSON body parser](#bodyparserjsonoptions)
|
||||||
|
* [Raw body parser](#bodyparserrawoptions)
|
||||||
|
* [Text body parser](#bodyparsertextoptions)
|
||||||
|
* [URL-encoded form body parser](#bodyparserurlencodedoptions)
|
||||||
|
|
||||||
|
Other body parsers you might be interested in:
|
||||||
|
|
||||||
|
- [body](https://www.npmjs.org/package/body#readme)
|
||||||
|
- [co-body](https://www.npmjs.org/package/co-body#readme)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ npm install body-parser
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
```
|
||||||
|
|
||||||
|
The `bodyParser` object exposes various factories to create middlewares. All
|
||||||
|
middlewares will populate the `req.body` property with the parsed body when
|
||||||
|
the `Content-Type` request header matches the `type` option.
|
||||||
|
|
||||||
|
The various errors returned by this module are described in the
|
||||||
|
[errors section](#errors).
|
||||||
|
|
||||||
|
### bodyParser.json([options])
|
||||||
|
|
||||||
|
Returns middleware that only parses `json` and only looks at requests where
|
||||||
|
the `Content-Type` header matches the `type` option. This parser accepts any
|
||||||
|
Unicode encoding of the body and supports automatic inflation of `gzip`,
|
||||||
|
`br` (brotli) and `deflate` encodings.
|
||||||
|
|
||||||
|
A new `body` object containing the parsed data is populated on the `request`
|
||||||
|
object after the middleware (i.e. `req.body`).
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
The `json` function takes an optional `options` object that may contain any of
|
||||||
|
the following keys:
|
||||||
|
|
||||||
|
##### inflate
|
||||||
|
|
||||||
|
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
||||||
|
`false`, deflated bodies are rejected. Defaults to `true`.
|
||||||
|
|
||||||
|
##### limit
|
||||||
|
|
||||||
|
Controls the maximum request body size. If this is a number, then the value
|
||||||
|
specifies the number of bytes; if it is a string, the value is passed to the
|
||||||
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
|
to `'100kb'`.
|
||||||
|
|
||||||
|
##### reviver
|
||||||
|
|
||||||
|
The `reviver` option is passed directly to `JSON.parse` as the second
|
||||||
|
argument. You can find more information on this argument
|
||||||
|
[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).
|
||||||
|
|
||||||
|
##### strict
|
||||||
|
|
||||||
|
When set to `true`, will only accept arrays and objects; when `false` will
|
||||||
|
accept anything `JSON.parse` accepts. Defaults to `true`.
|
||||||
|
|
||||||
|
##### type
|
||||||
|
|
||||||
|
The `type` option is used to determine what media type the middleware will
|
||||||
|
parse. This option can be a string, array of strings, or a function. If not a
|
||||||
|
function, `type` option is passed directly to the
|
||||||
|
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
|
||||||
|
be an extension name (like `json`), a mime type (like `application/json`), or
|
||||||
|
a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type`
|
||||||
|
option is called as `fn(req)` and the request is parsed if it returns a truthy
|
||||||
|
value. Defaults to `application/json`.
|
||||||
|
|
||||||
|
##### verify
|
||||||
|
|
||||||
|
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
|
||||||
|
where `buf` is a `Buffer` of the raw request body and `encoding` is the
|
||||||
|
encoding of the request. The parsing can be aborted by throwing an error.
|
||||||
|
|
||||||
|
### bodyParser.raw([options])
|
||||||
|
|
||||||
|
Returns middleware that parses all bodies as a `Buffer` and only looks at
|
||||||
|
requests where the `Content-Type` header matches the `type` option. This
|
||||||
|
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
|
||||||
|
encodings.
|
||||||
|
|
||||||
|
A new `body` object containing the parsed data is populated on the `request`
|
||||||
|
object after the middleware (i.e. `req.body`). This will be a `Buffer` object
|
||||||
|
of the body.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
The `raw` function takes an optional `options` object that may contain any of
|
||||||
|
the following keys:
|
||||||
|
|
||||||
|
##### inflate
|
||||||
|
|
||||||
|
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
||||||
|
`false`, deflated bodies are rejected. Defaults to `true`.
|
||||||
|
|
||||||
|
##### limit
|
||||||
|
|
||||||
|
Controls the maximum request body size. If this is a number, then the value
|
||||||
|
specifies the number of bytes; if it is a string, the value is passed to the
|
||||||
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
|
to `'100kb'`.
|
||||||
|
|
||||||
|
##### type
|
||||||
|
|
||||||
|
The `type` option is used to determine what media type the middleware will
|
||||||
|
parse. This option can be a string, array of strings, or a function.
|
||||||
|
If not a function, `type` option is passed directly to the
|
||||||
|
[type-is](https://www.npmjs.org/package/type-is#readme) library and this
|
||||||
|
can be an extension name (like `bin`), a mime type (like
|
||||||
|
`application/octet-stream`), or a mime type with a wildcard (like `*/*` or
|
||||||
|
`application/*`). If a function, the `type` option is called as `fn(req)`
|
||||||
|
and the request is parsed if it returns a truthy value. Defaults to
|
||||||
|
`application/octet-stream`.
|
||||||
|
|
||||||
|
##### verify
|
||||||
|
|
||||||
|
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
|
||||||
|
where `buf` is a `Buffer` of the raw request body and `encoding` is the
|
||||||
|
encoding of the request. The parsing can be aborted by throwing an error.
|
||||||
|
|
||||||
|
### bodyParser.text([options])
|
||||||
|
|
||||||
|
Returns middleware that parses all bodies as a string and only looks at
|
||||||
|
requests where the `Content-Type` header matches the `type` option. This
|
||||||
|
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
|
||||||
|
encodings.
|
||||||
|
|
||||||
|
A new `body` string containing the parsed data is populated on the `request`
|
||||||
|
object after the middleware (i.e. `req.body`). This will be a string of the
|
||||||
|
body.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
The `text` function takes an optional `options` object that may contain any of
|
||||||
|
the following keys:
|
||||||
|
|
||||||
|
##### defaultCharset
|
||||||
|
|
||||||
|
Specify the default character set for the text content if the charset is not
|
||||||
|
specified in the `Content-Type` header of the request. Defaults to `utf-8`.
|
||||||
|
|
||||||
|
##### inflate
|
||||||
|
|
||||||
|
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
||||||
|
`false`, deflated bodies are rejected. Defaults to `true`.
|
||||||
|
|
||||||
|
##### limit
|
||||||
|
|
||||||
|
Controls the maximum request body size. If this is a number, then the value
|
||||||
|
specifies the number of bytes; if it is a string, the value is passed to the
|
||||||
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
|
to `'100kb'`.
|
||||||
|
|
||||||
|
##### type
|
||||||
|
|
||||||
|
The `type` option is used to determine what media type the middleware will
|
||||||
|
parse. This option can be a string, array of strings, or a function. If not
|
||||||
|
a function, `type` option is passed directly to the
|
||||||
|
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
|
||||||
|
be an extension name (like `txt`), a mime type (like `text/plain`), or a mime
|
||||||
|
type with a wildcard (like `*/*` or `text/*`). If a function, the `type`
|
||||||
|
option is called as `fn(req)` and the request is parsed if it returns a
|
||||||
|
truthy value. Defaults to `text/plain`.
|
||||||
|
|
||||||
|
##### verify
|
||||||
|
|
||||||
|
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
|
||||||
|
where `buf` is a `Buffer` of the raw request body and `encoding` is the
|
||||||
|
encoding of the request. The parsing can be aborted by throwing an error.
|
||||||
|
|
||||||
|
### bodyParser.urlencoded([options])
|
||||||
|
|
||||||
|
Returns middleware that only parses `urlencoded` bodies and only looks at
|
||||||
|
requests where the `Content-Type` header matches the `type` option. This
|
||||||
|
parser accepts only UTF-8 encoding of the body and supports automatic
|
||||||
|
inflation of `gzip`, `br` (brotli) and `deflate` encodings.
|
||||||
|
|
||||||
|
A new `body` object containing the parsed data is populated on the `request`
|
||||||
|
object after the middleware (i.e. `req.body`). This object will contain
|
||||||
|
key-value pairs, where the value can be a string or array (when `extended` is
|
||||||
|
`false`), or any type (when `extended` is `true`).
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
The `urlencoded` function takes an optional `options` object that may contain
|
||||||
|
any of the following keys:
|
||||||
|
|
||||||
|
##### extended
|
||||||
|
|
||||||
|
The "extended" syntax allows for rich objects and arrays to be encoded into the
|
||||||
|
URL-encoded format, allowing for a JSON-like experience with URL-encoded. For
|
||||||
|
more information, please [see the qs
|
||||||
|
library](https://www.npmjs.org/package/qs#readme).
|
||||||
|
|
||||||
|
Defaults to `false`.
|
||||||
|
|
||||||
|
##### inflate
|
||||||
|
|
||||||
|
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
||||||
|
`false`, deflated bodies are rejected. Defaults to `true`.
|
||||||
|
|
||||||
|
##### limit
|
||||||
|
|
||||||
|
Controls the maximum request body size. If this is a number, then the value
|
||||||
|
specifies the number of bytes; if it is a string, the value is passed to the
|
||||||
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
|
to `'100kb'`.
|
||||||
|
|
||||||
|
##### parameterLimit
|
||||||
|
|
||||||
|
The `parameterLimit` option controls the maximum number of parameters that
|
||||||
|
are allowed in the URL-encoded data. If a request contains more parameters
|
||||||
|
than this value, a 413 will be returned to the client. Defaults to `1000`.
|
||||||
|
|
||||||
|
##### type
|
||||||
|
|
||||||
|
The `type` option is used to determine what media type the middleware will
|
||||||
|
parse. This option can be a string, array of strings, or a function. If not
|
||||||
|
a function, `type` option is passed directly to the
|
||||||
|
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
|
||||||
|
be an extension name (like `urlencoded`), a mime type (like
|
||||||
|
`application/x-www-form-urlencoded`), or a mime type with a wildcard (like
|
||||||
|
`*/x-www-form-urlencoded`). If a function, the `type` option is called as
|
||||||
|
`fn(req)` and the request is parsed if it returns a truthy value. Defaults
|
||||||
|
to `application/x-www-form-urlencoded`.
|
||||||
|
|
||||||
|
##### verify
|
||||||
|
|
||||||
|
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
|
||||||
|
where `buf` is a `Buffer` of the raw request body and `encoding` is the
|
||||||
|
encoding of the request. The parsing can be aborted by throwing an error.
|
||||||
|
|
||||||
|
##### defaultCharset
|
||||||
|
|
||||||
|
The default charset to parse as, if not specified in content-type. Must be
|
||||||
|
either `utf-8` or `iso-8859-1`. Defaults to `utf-8`.
|
||||||
|
|
||||||
|
##### charsetSentinel
|
||||||
|
|
||||||
|
Whether to let the value of the `utf8` parameter take precedence as the charset
|
||||||
|
selector. It requires the form to contain a parameter named `utf8` with a value
|
||||||
|
of `✓`. Defaults to `false`.
|
||||||
|
|
||||||
|
##### interpretNumericEntities
|
||||||
|
|
||||||
|
Whether to decode numeric entities such as `☺` when parsing an iso-8859-1
|
||||||
|
form. Defaults to `false`.
|
||||||
|
|
||||||
|
|
||||||
|
#### depth
|
||||||
|
|
||||||
|
The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible.
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
The middlewares provided by this module create errors using the
|
||||||
|
[`http-errors` module](https://www.npmjs.com/package/http-errors). The errors
|
||||||
|
will typically have a `status`/`statusCode` property that contains the suggested
|
||||||
|
HTTP response code, an `expose` property to determine if the `message` property
|
||||||
|
should be displayed to the client, a `type` property to determine the type of
|
||||||
|
error without matching against the `message`, and a `body` property containing
|
||||||
|
the read body, if available.
|
||||||
|
|
||||||
|
The following are the common errors created, though any error can come through
|
||||||
|
for various reasons.
|
||||||
|
|
||||||
|
### content encoding unsupported
|
||||||
|
|
||||||
|
This error will occur when the request had a `Content-Encoding` header that
|
||||||
|
contained an encoding but the "inflation" option was set to `false`. The
|
||||||
|
`status` property is set to `415`, the `type` property is set to
|
||||||
|
`'encoding.unsupported'`, and the `charset` property will be set to the
|
||||||
|
encoding that is unsupported.
|
||||||
|
|
||||||
|
### entity parse failed
|
||||||
|
|
||||||
|
This error will occur when the request contained an entity that could not be
|
||||||
|
parsed by the middleware. The `status` property is set to `400`, the `type`
|
||||||
|
property is set to `'entity.parse.failed'`, and the `body` property is set to
|
||||||
|
the entity value that failed parsing.
|
||||||
|
|
||||||
|
### entity verify failed
|
||||||
|
|
||||||
|
This error will occur when the request contained an entity that could not be
|
||||||
|
failed verification by the defined `verify` option. The `status` property is
|
||||||
|
set to `403`, the `type` property is set to `'entity.verify.failed'`, and the
|
||||||
|
`body` property is set to the entity value that failed verification.
|
||||||
|
|
||||||
|
### request aborted
|
||||||
|
|
||||||
|
This error will occur when the request is aborted by the client before reading
|
||||||
|
the body has finished. The `received` property will be set to the number of
|
||||||
|
bytes received before the request was aborted and the `expected` property is
|
||||||
|
set to the number of expected bytes. The `status` property is set to `400`
|
||||||
|
and `type` property is set to `'request.aborted'`.
|
||||||
|
|
||||||
|
### request entity too large
|
||||||
|
|
||||||
|
This error will occur when the request body's size is larger than the "limit"
|
||||||
|
option. The `limit` property will be set to the byte limit and the `length`
|
||||||
|
property will be set to the request body's length. The `status` property is
|
||||||
|
set to `413` and the `type` property is set to `'entity.too.large'`.
|
||||||
|
|
||||||
|
### request size did not match content length
|
||||||
|
|
||||||
|
This error will occur when the request's length did not match the length from
|
||||||
|
the `Content-Length` header. This typically occurs when the request is malformed,
|
||||||
|
typically when the `Content-Length` header was calculated based on characters
|
||||||
|
instead of bytes. The `status` property is set to `400` and the `type` property
|
||||||
|
is set to `'request.size.invalid'`.
|
||||||
|
|
||||||
|
### stream encoding should not be set
|
||||||
|
|
||||||
|
This error will occur when something called the `req.setEncoding` method prior
|
||||||
|
to this middleware. This module operates directly on bytes only and you cannot
|
||||||
|
call `req.setEncoding` when using this module. The `status` property is set to
|
||||||
|
`500` and the `type` property is set to `'stream.encoding.set'`.
|
||||||
|
|
||||||
|
### stream is not readable
|
||||||
|
|
||||||
|
This error will occur when the request is no longer readable when this middleware
|
||||||
|
attempts to read it. This typically means something other than a middleware from
|
||||||
|
this module read the request body already and the middleware was also configured to
|
||||||
|
read the same request. The `status` property is set to `500` and the `type`
|
||||||
|
property is set to `'stream.not.readable'`.
|
||||||
|
|
||||||
|
### too many parameters
|
||||||
|
|
||||||
|
This error will occur when the content of the request exceeds the configured
|
||||||
|
`parameterLimit` for the `urlencoded` parser. The `status` property is set to
|
||||||
|
`413` and the `type` property is set to `'parameters.too.many'`.
|
||||||
|
|
||||||
|
### unsupported charset "BOGUS"
|
||||||
|
|
||||||
|
This error will occur when the request had a charset parameter in the
|
||||||
|
`Content-Type` header, but the `iconv-lite` module does not support it OR the
|
||||||
|
parser does not support it. The charset is contained in the message as well
|
||||||
|
as in the `charset` property. The `status` property is set to `415`, the
|
||||||
|
`type` property is set to `'charset.unsupported'`, and the `charset` property
|
||||||
|
is set to the charset that is unsupported.
|
||||||
|
|
||||||
|
### unsupported content encoding "bogus"
|
||||||
|
|
||||||
|
This error will occur when the request had a `Content-Encoding` header that
|
||||||
|
contained an unsupported encoding. The encoding is contained in the message
|
||||||
|
as well as in the `encoding` property. The `status` property is set to `415`,
|
||||||
|
the `type` property is set to `'encoding.unsupported'`, and the `encoding`
|
||||||
|
property is set to the encoding that is unsupported.
|
||||||
|
|
||||||
|
### The input exceeded the depth
|
||||||
|
|
||||||
|
This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Express/Connect top-level generic
|
||||||
|
|
||||||
|
This example demonstrates adding a generic JSON and URL-encoded parser as a
|
||||||
|
top-level middleware, which will parse the bodies of all incoming requests.
|
||||||
|
This is the simplest setup.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const express = require('express')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
// parse application/x-www-form-urlencoded
|
||||||
|
app.use(bodyParser.urlencoded())
|
||||||
|
|
||||||
|
// parse application/json
|
||||||
|
app.use(bodyParser.json())
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.setHeader('Content-Type', 'text/plain')
|
||||||
|
res.write('you posted:\n')
|
||||||
|
res.end(String(JSON.stringify(req.body, null, 2)))
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Express route-specific
|
||||||
|
|
||||||
|
This example demonstrates adding body parsers specifically to the routes that
|
||||||
|
need them. In general, this is the most recommended way to use body-parser with
|
||||||
|
Express.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const express = require('express')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
// create application/json parser
|
||||||
|
const jsonParser = bodyParser.json()
|
||||||
|
|
||||||
|
// create application/x-www-form-urlencoded parser
|
||||||
|
const urlencodedParser = bodyParser.urlencoded()
|
||||||
|
|
||||||
|
// POST /login gets urlencoded bodies
|
||||||
|
app.post('/login', urlencodedParser, function (req, res) {
|
||||||
|
if (!req.body || !req.body.username) res.sendStatus(400)
|
||||||
|
res.send('welcome, ' + req.body.username)
|
||||||
|
})
|
||||||
|
|
||||||
|
// POST /api/users gets JSON bodies
|
||||||
|
app.post('/api/users', jsonParser, function (req, res) {
|
||||||
|
if (!req.body) res.sendStatus(400)
|
||||||
|
// create user in req.body
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change accepted type for parsers
|
||||||
|
|
||||||
|
All the parsers accept a `type` option which allows you to change the
|
||||||
|
`Content-Type` that the middleware will parse.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const express = require('express')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
// parse various different custom JSON types as JSON
|
||||||
|
app.use(bodyParser.json({ type: 'application/*+json' }))
|
||||||
|
|
||||||
|
// parse some custom thing into a Buffer
|
||||||
|
app.use(bodyParser.raw({ type: 'application/vnd.custom-type' }))
|
||||||
|
|
||||||
|
// parse an HTML body into a string
|
||||||
|
app.use(bodyParser.text({ type: 'text/html' }))
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[ci-image]: https://badgen.net/github/checks/expressjs/body-parser/master?label=ci
|
||||||
|
[ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml
|
||||||
|
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/body-parser/master
|
||||||
|
[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master
|
||||||
|
[node-version-image]: https://badgen.net/npm/node/body-parser
|
||||||
|
[node-version-url]: https://nodejs.org/en/download
|
||||||
|
[npm-downloads-image]: https://badgen.net/npm/dm/body-parser
|
||||||
|
[npm-url]: https://npmjs.org/package/body-parser
|
||||||
|
[npm-version-image]: https://badgen.net/npm/v/body-parser
|
||||||
|
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/body-parser/badge
|
||||||
|
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/body-parser
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2016 Douglas Christopher Wilson
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "negotiator",
|
||||||
|
"description": "HTTP content negotiation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"contributors": [
|
||||||
|
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||||
|
"Federico Romero <federico.romero@outboxlabs.com>",
|
||||||
|
"Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"http",
|
||||||
|
"content negotiation",
|
||||||
|
"accept",
|
||||||
|
"accept-language",
|
||||||
|
"accept-encoding",
|
||||||
|
"accept-charset"
|
||||||
|
],
|
||||||
|
"repository": "jshttp/negotiator",
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "7.32.0",
|
||||||
|
"eslint-plugin-markdown": "2.2.1",
|
||||||
|
"mocha": "9.1.3",
|
||||||
|
"nyc": "15.1.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib/",
|
||||||
|
"HISTORY.md",
|
||||||
|
"LICENSE",
|
||||||
|
"index.js",
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"test": "mocha --reporter spec --check-leaks --bail test/",
|
||||||
|
"test:debug": "mocha --reporter spec --check-leaks --inspect --inspect-brk test/",
|
||||||
|
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
|
||||||
|
"test-cov": "nyc --reporter=html --reporter=text npm test"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var inspect = require('../');
|
||||||
|
var test = require('tape');
|
||||||
|
|
||||||
|
test('quoteStyle option', function (t) {
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: false }); }, 'false is not a valid value');
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: true }); }, 'true is not a valid value');
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: '' }); }, '"" is not a valid value');
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: {} }); }, '{} is not a valid value');
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: [] }); }, '[] is not a valid value');
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: 42 }); }, '42 is not a valid value');
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: NaN }); }, 'NaN is not a valid value');
|
||||||
|
t['throws'](function () { inspect(null, { quoteStyle: function () {} }); }, 'a function is not a valid value');
|
||||||
|
|
||||||
|
t.equal(inspect('"', { quoteStyle: 'single' }), '\'"\'', 'double quote, quoteStyle: "single"');
|
||||||
|
t.equal(inspect('"', { quoteStyle: 'double' }), '"\\""', 'double quote, quoteStyle: "double"');
|
||||||
|
|
||||||
|
t.equal(inspect('\'', { quoteStyle: 'single' }), '\'\\\'\'', 'single quote, quoteStyle: "single"');
|
||||||
|
t.equal(inspect('\'', { quoteStyle: 'double' }), '"\'"', 'single quote, quoteStyle: "double"');
|
||||||
|
|
||||||
|
t.equal(inspect('`', { quoteStyle: 'single' }), '\'`\'', 'backtick, quoteStyle: "single"');
|
||||||
|
t.equal(inspect('`', { quoteStyle: 'double' }), '"`"', 'backtick, quoteStyle: "double"');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
@ -0,0 +1,708 @@
|
|||||||
|
<h1 align="center">Picomatch</h1>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://npmjs.org/package/picomatch">
|
||||||
|
<img src="https://img.shields.io/npm/v/picomatch.svg" alt="version">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/micromatch/picomatch/actions?workflow=Tests">
|
||||||
|
<img src="https://github.com/micromatch/picomatch/workflows/Tests/badge.svg" alt="test status">
|
||||||
|
</a>
|
||||||
|
<a href="https://coveralls.io/github/micromatch/picomatch">
|
||||||
|
<img src="https://img.shields.io/coveralls/github/micromatch/picomatch/master.svg" alt="coverage status">
|
||||||
|
</a>
|
||||||
|
<a href="https://npmjs.org/package/picomatch">
|
||||||
|
<img src="https://img.shields.io/npm/dm/picomatch.svg" alt="downloads">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<strong>Blazing fast and accurate glob matcher written in JavaScript.</strong></br>
|
||||||
|
<em>No dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.</em>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Why picomatch?
|
||||||
|
|
||||||
|
* **Lightweight** - No dependencies
|
||||||
|
* **Minimal** - Tiny API surface. Main export is a function that takes a glob pattern and returns a matcher function.
|
||||||
|
* **Fast** - Loads in about 2ms (that's several times faster than a [single frame of a HD movie](http://www.endmemo.com/sconvert/framespersecondframespermillisecond.php) at 60fps)
|
||||||
|
* **Performant** - Use the returned matcher function to speed up repeat matching (like when watching files)
|
||||||
|
* **Accurate matching** - Using wildcards (`*` and `?`), globstars (`**`) for nested directories, [advanced globbing](#advanced-globbing) with extglobs, braces, and POSIX brackets, and support for escaping special characters with `\` or quotes.
|
||||||
|
* **Well tested** - Thousands of unit tests
|
||||||
|
|
||||||
|
See the [library comparison](#library-comparisons) to other libraries.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
<details><summary> Click to expand </summary>
|
||||||
|
|
||||||
|
- [Install](#install)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [API](#api)
|
||||||
|
* [picomatch](#picomatch)
|
||||||
|
* [.test](#test)
|
||||||
|
* [.matchBase](#matchbase)
|
||||||
|
* [.isMatch](#ismatch)
|
||||||
|
* [.parse](#parse)
|
||||||
|
* [.scan](#scan)
|
||||||
|
* [.compileRe](#compilere)
|
||||||
|
* [.makeRe](#makere)
|
||||||
|
* [.toRegex](#toregex)
|
||||||
|
- [Options](#options)
|
||||||
|
* [Picomatch options](#picomatch-options)
|
||||||
|
* [Scan Options](#scan-options)
|
||||||
|
* [Options Examples](#options-examples)
|
||||||
|
- [Globbing features](#globbing-features)
|
||||||
|
* [Basic globbing](#basic-globbing)
|
||||||
|
* [Advanced globbing](#advanced-globbing)
|
||||||
|
* [Braces](#braces)
|
||||||
|
* [Matching special characters as literals](#matching-special-characters-as-literals)
|
||||||
|
- [Library Comparisons](#library-comparisons)
|
||||||
|
- [Benchmarks](#benchmarks)
|
||||||
|
- [Philosophies](#philosophies)
|
||||||
|
- [About](#about)
|
||||||
|
* [Author](#author)
|
||||||
|
* [License](#license)
|
||||||
|
|
||||||
|
_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Install with [npm](https://www.npmjs.com/):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save picomatch
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The main export is a function that takes a glob pattern and an options object and returns a function for matching strings.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const pm = require('picomatch');
|
||||||
|
const isMatch = pm('*.js');
|
||||||
|
|
||||||
|
console.log(isMatch('abcd')); //=> false
|
||||||
|
console.log(isMatch('a.js')); //=> true
|
||||||
|
console.log(isMatch('a.md')); //=> false
|
||||||
|
console.log(isMatch('a/b.js')); //=> false
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### [picomatch](lib/picomatch.js#L32)
|
||||||
|
|
||||||
|
Creates a matcher function from one or more glob patterns. The returned function takes a string to match as its first argument, and returns true if the string is a match. The returned matcher function also takes a boolean as the second argument that, when true, returns an object with additional information.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `globs` **{String|Array}**: One or more glob patterns.
|
||||||
|
* `options` **{Object=}**
|
||||||
|
* `returns` **{Function=}**: Returns a matcher function.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
// picomatch(glob[, options]);
|
||||||
|
|
||||||
|
const isMatch = picomatch('*.!(*a)');
|
||||||
|
console.log(isMatch('a.a')); //=> false
|
||||||
|
console.log(isMatch('a.b')); //=> true
|
||||||
|
```
|
||||||
|
|
||||||
|
### [.test](lib/picomatch.js#L117)
|
||||||
|
|
||||||
|
Test `input` with the given `regex`. This is used by the main `picomatch()` function to test the input string.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `input` **{String}**: String to test.
|
||||||
|
* `regex` **{RegExp}**
|
||||||
|
* `returns` **{Object}**: Returns an object with matching info.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
// picomatch.test(input, regex[, options]);
|
||||||
|
|
||||||
|
console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
|
||||||
|
// { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
|
||||||
|
```
|
||||||
|
|
||||||
|
### [.matchBase](lib/picomatch.js#L161)
|
||||||
|
|
||||||
|
Match the basename of a filepath.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `input` **{String}**: String to test.
|
||||||
|
* `glob` **{RegExp|String}**: Glob pattern or regex created by [.makeRe](#makeRe).
|
||||||
|
* `returns` **{Boolean}**
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
// picomatch.matchBase(input, glob[, options]);
|
||||||
|
console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
|
||||||
|
```
|
||||||
|
|
||||||
|
### [.isMatch](lib/picomatch.js#L183)
|
||||||
|
|
||||||
|
Returns true if **any** of the given glob `patterns` match the specified `string`.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* **{String|Array}**: str The string to test.
|
||||||
|
* **{String|Array}**: patterns One or more glob patterns to use for matching.
|
||||||
|
* **{Object}**: See available [options](#options).
|
||||||
|
* `returns` **{Boolean}**: Returns true if any patterns match `str`
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
// picomatch.isMatch(string, patterns[, options]);
|
||||||
|
|
||||||
|
console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
|
||||||
|
console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
|
||||||
|
```
|
||||||
|
|
||||||
|
### [.parse](lib/picomatch.js#L199)
|
||||||
|
|
||||||
|
Parse a glob pattern to create the source string for a regular expression.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `pattern` **{String}**
|
||||||
|
* `options` **{Object}**
|
||||||
|
* `returns` **{Object}**: Returns an object with useful properties and output to be used as a regex source string.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
const result = picomatch.parse(pattern[, options]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### [.scan](lib/picomatch.js#L231)
|
||||||
|
|
||||||
|
Scan a glob pattern to separate the pattern into segments.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `input` **{String}**: Glob pattern to scan.
|
||||||
|
* `options` **{Object}**
|
||||||
|
* `returns` **{Object}**: Returns an object with
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
// picomatch.scan(input[, options]);
|
||||||
|
|
||||||
|
const result = picomatch.scan('!./foo/*.js');
|
||||||
|
console.log(result);
|
||||||
|
{ prefix: '!./',
|
||||||
|
input: '!./foo/*.js',
|
||||||
|
start: 3,
|
||||||
|
base: 'foo',
|
||||||
|
glob: '*.js',
|
||||||
|
isBrace: false,
|
||||||
|
isBracket: false,
|
||||||
|
isGlob: true,
|
||||||
|
isExtglob: false,
|
||||||
|
isGlobstar: false,
|
||||||
|
negated: true }
|
||||||
|
```
|
||||||
|
|
||||||
|
### [.compileRe](lib/picomatch.js#L245)
|
||||||
|
|
||||||
|
Compile a regular expression from the `state` object returned by the
|
||||||
|
[parse()](#parse) method.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `state` **{Object}**
|
||||||
|
* `options` **{Object}**
|
||||||
|
* `returnOutput` **{Boolean}**: Intended for implementors, this argument allows you to return the raw output from the parser.
|
||||||
|
* `returnState` **{Boolean}**: Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
|
||||||
|
* `returns` **{RegExp}**
|
||||||
|
|
||||||
|
### [.makeRe](lib/picomatch.js#L286)
|
||||||
|
|
||||||
|
Create a regular expression from a parsed glob pattern.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `state` **{String}**: The object returned from the `.parse` method.
|
||||||
|
* `options` **{Object}**
|
||||||
|
* `returnOutput` **{Boolean}**: Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
|
||||||
|
* `returnState` **{Boolean}**: Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
|
||||||
|
* `returns` **{RegExp}**: Returns a regex created from the given pattern.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
const state = picomatch.parse('*.js');
|
||||||
|
// picomatch.compileRe(state[, options]);
|
||||||
|
|
||||||
|
console.log(picomatch.compileRe(state));
|
||||||
|
//=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
||||||
|
```
|
||||||
|
|
||||||
|
### [.toRegex](lib/picomatch.js#L321)
|
||||||
|
|
||||||
|
Create a regular expression from the given regex source string.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
* `source` **{String}**: Regular expression source string.
|
||||||
|
* `options` **{Object}**
|
||||||
|
* `returns` **{RegExp}**
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
// picomatch.toRegex(source[, options]);
|
||||||
|
|
||||||
|
const { output } = picomatch.parse('*.js');
|
||||||
|
console.log(picomatch.toRegex(output));
|
||||||
|
//=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### Picomatch options
|
||||||
|
|
||||||
|
The following options may be used with the main `picomatch()` function or any of the methods on the picomatch API.
|
||||||
|
|
||||||
|
| **Option** | **Type** | **Default value** | **Description** |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `basename` | `boolean` | `false` | If set, then patterns without slashes will be matched against the basename of the path if it contains slashes. For example, `a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`. |
|
||||||
|
| `bash` | `boolean` | `false` | Follow bash matching rules more strictly - disallows backslashes as escape characters, and treats single stars as globstars (`**`). |
|
||||||
|
| `capture` | `boolean` | `undefined` | Return regex matches in supporting methods. |
|
||||||
|
| `contains` | `boolean` | `undefined` | Allows glob to match any part of the given string(s). |
|
||||||
|
| `cwd` | `string` | `process.cwd()` | Current working directory. Used by `picomatch.split()` |
|
||||||
|
| `debug` | `boolean` | `undefined` | Debug regular expressions when an error is thrown. |
|
||||||
|
| `dot` | `boolean` | `false` | Enable dotfile matching. By default, dotfiles are ignored unless a `.` is explicitly defined in the pattern, or `options.dot` is true |
|
||||||
|
| `expandRange` | `function` | `undefined` | Custom function for expanding ranges in brace patterns, such as `{a..z}`. The function receives the range values as two arguments, and it must return a string to be used in the generated regex. It's recommended that returned strings be wrapped in parentheses. |
|
||||||
|
| `failglob` | `boolean` | `false` | Throws an error if no matches are found. Based on the bash option of the same name. |
|
||||||
|
| `fastpaths` | `boolean` | `true` | To speed up processing, full parsing is skipped for a handful common glob patterns. Disable this behavior by setting this option to `false`. |
|
||||||
|
| `flags` | `string` | `undefined` | Regex flags to use in the generated regex. If defined, the `nocase` option will be overridden. |
|
||||||
|
| [format](#optionsformat) | `function` | `undefined` | Custom function for formatting the returned string. This is useful for removing leading slashes, converting Windows paths to Posix paths, etc. |
|
||||||
|
| `ignore` | `array\|string` | `undefined` | One or more glob patterns for excluding strings that should not be matched from the result. |
|
||||||
|
| `keepQuotes` | `boolean` | `false` | Retain quotes in the generated regex, since quotes may also be used as an alternative to backslashes. |
|
||||||
|
| `literalBrackets` | `boolean` | `undefined` | When `true`, brackets in the glob pattern will be escaped so that only literal brackets will be matched. |
|
||||||
|
| `matchBase` | `boolean` | `false` | Alias for `basename` |
|
||||||
|
| `maxLength` | `boolean` | `65536` | Limit the max length of the input string. An error is thrown if the input string is longer than this value. |
|
||||||
|
| `nobrace` | `boolean` | `false` | Disable brace matching, so that `{a,b}` and `{1..3}` would be treated as literal characters. |
|
||||||
|
| `nobracket` | `boolean` | `undefined` | Disable matching with regex brackets. |
|
||||||
|
| `nocase` | `boolean` | `false` | Make matching case-insensitive. Equivalent to the regex `i` flag. Note that this option is overridden by the `flags` option. |
|
||||||
|
| `nodupes` | `boolean` | `true` | Deprecated, use `nounique` instead. This option will be removed in a future major release. By default duplicates are removed. Disable uniquification by setting this option to false. |
|
||||||
|
| `noext` | `boolean` | `false` | Alias for `noextglob` |
|
||||||
|
| `noextglob` | `boolean` | `false` | Disable support for matching with extglobs (like `+(a\|b)`) |
|
||||||
|
| `noglobstar` | `boolean` | `false` | Disable support for matching nested directories with globstars (`**`) |
|
||||||
|
| `nonegate` | `boolean` | `false` | Disable support for negating with leading `!` |
|
||||||
|
| `noquantifiers` | `boolean` | `false` | Disable support for regex quantifiers (like `a{1,2}`) and treat them as brace patterns to be expanded. |
|
||||||
|
| [onIgnore](#optionsonIgnore) | `function` | `undefined` | Function to be called on ignored items. |
|
||||||
|
| [onMatch](#optionsonMatch) | `function` | `undefined` | Function to be called on matched items. |
|
||||||
|
| [onResult](#optionsonResult) | `function` | `undefined` | Function to be called on all items, regardless of whether or not they are matched or ignored. |
|
||||||
|
| `posix` | `boolean` | `false` | Support POSIX character classes ("posix brackets"). |
|
||||||
|
| `posixSlashes` | `boolean` | `undefined` | Convert all slashes in file paths to forward slashes. This does not convert slashes in the glob pattern itself |
|
||||||
|
| `prepend` | `boolean` | `undefined` | String to prepend to the generated regex used for matching. |
|
||||||
|
| `regex` | `boolean` | `false` | Use regular expression rules for `+` (instead of matching literal `+`), and for stars that follow closing parentheses or brackets (as in `)*` and `]*`). |
|
||||||
|
| `strictBrackets` | `boolean` | `undefined` | Throw an error if brackets, braces, or parens are imbalanced. |
|
||||||
|
| `strictSlashes` | `boolean` | `undefined` | When true, picomatch won't match trailing slashes with single stars. |
|
||||||
|
| `unescape` | `boolean` | `undefined` | Remove backslashes preceding escaped characters in the glob pattern. By default, backslashes are retained. |
|
||||||
|
| `unixify` | `boolean` | `undefined` | Alias for `posixSlashes`, for backwards compatibility. |
|
||||||
|
|
||||||
|
picomatch has automatic detection for regex positive and negative lookbehinds. If the pattern contains a negative lookbehind, you must be using Node.js >= 8.10 or else picomatch will throw an error.
|
||||||
|
|
||||||
|
### Scan Options
|
||||||
|
|
||||||
|
In addition to the main [picomatch options](#picomatch-options), the following options may also be used with the [.scan](#scan) method.
|
||||||
|
|
||||||
|
| **Option** | **Type** | **Default value** | **Description** |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `tokens` | `boolean` | `false` | When `true`, the returned object will include an array of tokens (objects), representing each path "segment" in the scanned glob pattern |
|
||||||
|
| `parts` | `boolean` | `false` | When `true`, the returned object will include an array of strings representing each path "segment" in the scanned glob pattern. This is automatically enabled when `options.tokens` is true |
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
const result = picomatch.scan('!./foo/*.js', { tokens: true });
|
||||||
|
console.log(result);
|
||||||
|
// {
|
||||||
|
// prefix: '!./',
|
||||||
|
// input: '!./foo/*.js',
|
||||||
|
// start: 3,
|
||||||
|
// base: 'foo',
|
||||||
|
// glob: '*.js',
|
||||||
|
// isBrace: false,
|
||||||
|
// isBracket: false,
|
||||||
|
// isGlob: true,
|
||||||
|
// isExtglob: false,
|
||||||
|
// isGlobstar: false,
|
||||||
|
// negated: true,
|
||||||
|
// maxDepth: 2,
|
||||||
|
// tokens: [
|
||||||
|
// { value: '!./', depth: 0, isGlob: false, negated: true, isPrefix: true },
|
||||||
|
// { value: 'foo', depth: 1, isGlob: false },
|
||||||
|
// { value: '*.js', depth: 1, isGlob: true }
|
||||||
|
// ],
|
||||||
|
// slashes: [ 2, 6 ],
|
||||||
|
// parts: [ 'foo', '*.js' ]
|
||||||
|
// }
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Options Examples
|
||||||
|
|
||||||
|
#### options.expandRange
|
||||||
|
|
||||||
|
**Type**: `function`
|
||||||
|
|
||||||
|
**Default**: `undefined`
|
||||||
|
|
||||||
|
Custom function for expanding ranges in brace patterns. The [fill-range](https://github.com/jonschlinkert/fill-range) library is ideal for this purpose, or you can use custom code to do whatever you need.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
The following example shows how to create a glob that matches a folder
|
||||||
|
|
||||||
|
```js
|
||||||
|
const fill = require('fill-range');
|
||||||
|
const regex = pm.makeRe('foo/{01..25}/bar', {
|
||||||
|
expandRange(a, b) {
|
||||||
|
return `(${fill(a, b, { toRegex: true })})`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(regex);
|
||||||
|
//=> /^(?:foo\/((?:0[1-9]|1[0-9]|2[0-5]))\/bar)$/
|
||||||
|
|
||||||
|
console.log(regex.test('foo/00/bar')) // false
|
||||||
|
console.log(regex.test('foo/01/bar')) // true
|
||||||
|
console.log(regex.test('foo/10/bar')) // true
|
||||||
|
console.log(regex.test('foo/22/bar')) // true
|
||||||
|
console.log(regex.test('foo/25/bar')) // true
|
||||||
|
console.log(regex.test('foo/26/bar')) // false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### options.format
|
||||||
|
|
||||||
|
**Type**: `function`
|
||||||
|
|
||||||
|
**Default**: `undefined`
|
||||||
|
|
||||||
|
Custom function for formatting strings before they're matched.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```js
|
||||||
|
// strip leading './' from strings
|
||||||
|
const format = str => str.replace(/^\.\//, '');
|
||||||
|
const isMatch = picomatch('foo/*.js', { format });
|
||||||
|
console.log(isMatch('./foo/bar.js')); //=> true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### options.onMatch
|
||||||
|
|
||||||
|
```js
|
||||||
|
const onMatch = ({ glob, regex, input, output }) => {
|
||||||
|
console.log({ glob, regex, input, output });
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMatch = picomatch('*', { onMatch });
|
||||||
|
isMatch('foo');
|
||||||
|
isMatch('bar');
|
||||||
|
isMatch('baz');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### options.onIgnore
|
||||||
|
|
||||||
|
```js
|
||||||
|
const onIgnore = ({ glob, regex, input, output }) => {
|
||||||
|
console.log({ glob, regex, input, output });
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMatch = picomatch('*', { onIgnore, ignore: 'f*' });
|
||||||
|
isMatch('foo');
|
||||||
|
isMatch('bar');
|
||||||
|
isMatch('baz');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### options.onResult
|
||||||
|
|
||||||
|
```js
|
||||||
|
const onResult = ({ glob, regex, input, output }) => {
|
||||||
|
console.log({ glob, regex, input, output });
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMatch = picomatch('*', { onResult, ignore: 'f*' });
|
||||||
|
isMatch('foo');
|
||||||
|
isMatch('bar');
|
||||||
|
isMatch('baz');
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Globbing features
|
||||||
|
|
||||||
|
* [Basic globbing](#basic-globbing) (Wildcard matching)
|
||||||
|
* [Advanced globbing](#advanced-globbing) (extglobs, posix brackets, brace matching)
|
||||||
|
|
||||||
|
### Basic globbing
|
||||||
|
|
||||||
|
| **Character** | **Description** |
|
||||||
|
| --- | --- |
|
||||||
|
| `*` | Matches any character zero or more times, excluding path separators. Does _not match_ path separators or hidden files or directories ("dotfiles"), unless explicitly enabled by setting the `dot` option to `true`. |
|
||||||
|
| `**` | Matches any character zero or more times, including path separators. Note that `**` will only match path separators (`/`, and `\\` on Windows) when they are the only characters in a path segment. Thus, `foo**/bar` is equivalent to `foo*/bar`, and `foo/a**b/bar` is equivalent to `foo/a*b/bar`, and _more than two_ consecutive stars in a glob path segment are regarded as _a single star_. Thus, `foo/***/bar` is equivalent to `foo/*/bar`. |
|
||||||
|
| `?` | Matches any character excluding path separators one time. Does _not match_ path separators or leading dots. |
|
||||||
|
| `[abc]` | Matches any characters inside the brackets. For example, `[abc]` would match the characters `a`, `b` or `c`, and nothing else. |
|
||||||
|
|
||||||
|
#### Matching behavior vs. Bash
|
||||||
|
|
||||||
|
Picomatch's matching features and expected results in unit tests are based on Bash's unit tests and the Bash 4.3 specification, with the following exceptions:
|
||||||
|
|
||||||
|
* Bash will match `foo/bar/baz` with `*`. Picomatch only matches nested directories with `**`.
|
||||||
|
* Bash greedily matches with negated extglobs. For example, Bash 4.3 says that `!(foo)*` should match `foo` and `foobar`, since the trailing `*` bracktracks to match the preceding pattern. This is very memory-inefficient, and IMHO, also incorrect. Picomatch would return `false` for both `foo` and `foobar`.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Advanced globbing
|
||||||
|
|
||||||
|
* [extglobs](#extglobs)
|
||||||
|
* [POSIX brackets](#posix-brackets)
|
||||||
|
* [Braces](#brace-expansion)
|
||||||
|
|
||||||
|
#### Extglobs
|
||||||
|
|
||||||
|
| **Pattern** | **Description** |
|
||||||
|
| --- | --- |
|
||||||
|
| `@(pattern)` | Match _only one_ consecutive occurrence of `pattern` |
|
||||||
|
| `*(pattern)` | Match _zero or more_ consecutive occurrences of `pattern` |
|
||||||
|
| `+(pattern)` | Match _one or more_ consecutive occurrences of `pattern` |
|
||||||
|
| `?(pattern)` | Match _zero or **one**_ consecutive occurrences of `pattern` |
|
||||||
|
| `!(pattern)` | Match _anything but_ `pattern` |
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const pm = require('picomatch');
|
||||||
|
|
||||||
|
// *(pattern) matches ZERO or more of "pattern"
|
||||||
|
console.log(pm.isMatch('a', 'a*(z)')); // true
|
||||||
|
console.log(pm.isMatch('az', 'a*(z)')); // true
|
||||||
|
console.log(pm.isMatch('azzz', 'a*(z)')); // true
|
||||||
|
|
||||||
|
// +(pattern) matches ONE or more of "pattern"
|
||||||
|
console.log(pm.isMatch('a', 'a*(z)')); // true
|
||||||
|
console.log(pm.isMatch('az', 'a*(z)')); // true
|
||||||
|
console.log(pm.isMatch('azzz', 'a*(z)')); // true
|
||||||
|
|
||||||
|
// supports multiple extglobs
|
||||||
|
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // false
|
||||||
|
|
||||||
|
// supports nested extglobs
|
||||||
|
console.log(pm.isMatch('foo.bar', '!(!(foo)).!(!(bar))')); // true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POSIX brackets
|
||||||
|
|
||||||
|
POSIX classes are disabled by default. Enable this feature by setting the `posix` option to true.
|
||||||
|
|
||||||
|
**Enable POSIX bracket support**
|
||||||
|
|
||||||
|
```js
|
||||||
|
console.log(pm.makeRe('[[:word:]]+', { posix: true }));
|
||||||
|
//=> /^(?:(?=.)[A-Za-z0-9_]+\/?)$/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Supported POSIX classes**
|
||||||
|
|
||||||
|
The following named POSIX bracket expressions are supported:
|
||||||
|
|
||||||
|
* `[:alnum:]` - Alphanumeric characters, equ `[a-zA-Z0-9]`
|
||||||
|
* `[:alpha:]` - Alphabetical characters, equivalent to `[a-zA-Z]`.
|
||||||
|
* `[:ascii:]` - ASCII characters, equivalent to `[\\x00-\\x7F]`.
|
||||||
|
* `[:blank:]` - Space and tab characters, equivalent to `[ \\t]`.
|
||||||
|
* `[:cntrl:]` - Control characters, equivalent to `[\\x00-\\x1F\\x7F]`.
|
||||||
|
* `[:digit:]` - Numerical digits, equivalent to `[0-9]`.
|
||||||
|
* `[:graph:]` - Graph characters, equivalent to `[\\x21-\\x7E]`.
|
||||||
|
* `[:lower:]` - Lowercase letters, equivalent to `[a-z]`.
|
||||||
|
* `[:print:]` - Print characters, equivalent to `[\\x20-\\x7E ]`.
|
||||||
|
* `[:punct:]` - Punctuation and symbols, equivalent to `[\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~]`.
|
||||||
|
* `[:space:]` - Extended space characters, equivalent to `[ \\t\\r\\n\\v\\f]`.
|
||||||
|
* `[:upper:]` - Uppercase letters, equivalent to `[A-Z]`.
|
||||||
|
* `[:word:]` - Word characters (letters, numbers and underscores), equivalent to `[A-Za-z0-9_]`.
|
||||||
|
* `[:xdigit:]` - Hexadecimal digits, equivalent to `[A-Fa-f0-9]`.
|
||||||
|
|
||||||
|
See the [Bash Reference Manual](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html) for more information.
|
||||||
|
|
||||||
|
### Braces
|
||||||
|
|
||||||
|
Picomatch does not do brace expansion. For [brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html) and advanced matching with braces, use [micromatch](https://github.com/micromatch/micromatch) instead. Picomatch has very basic support for braces.
|
||||||
|
|
||||||
|
### Matching special characters as literals
|
||||||
|
|
||||||
|
If you wish to match the following special characters in a filepath, and you want to use these characters in your glob pattern, they must be escaped with backslashes or quotes:
|
||||||
|
|
||||||
|
**Special Characters**
|
||||||
|
|
||||||
|
Some characters that are used for matching in regular expressions are also regarded as valid file path characters on some platforms.
|
||||||
|
|
||||||
|
To match any of the following characters as literals: `$^*+?()[]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```js
|
||||||
|
console.log(pm.makeRe('foo/bar \\(1\\)'));
|
||||||
|
console.log(pm.makeRe('foo/bar \\(1\\)'));
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Library Comparisons
|
||||||
|
|
||||||
|
The following table shows which features are supported by [minimatch](https://github.com/isaacs/minimatch), [micromatch](https://github.com/micromatch/micromatch), [picomatch](https://github.com/micromatch/picomatch), [nanomatch](https://github.com/micromatch/nanomatch), [extglob](https://github.com/micromatch/extglob), [braces](https://github.com/micromatch/braces), and [expand-brackets](https://github.com/micromatch/expand-brackets).
|
||||||
|
|
||||||
|
| **Feature** | `minimatch` | `micromatch` | `picomatch` | `nanomatch` | `extglob` | `braces` | `expand-brackets` |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| Wildcard matching (`*?+`) | ✔ | ✔ | ✔ | ✔ | - | - | - |
|
||||||
|
| Advancing globbing | ✔ | ✔ | ✔ | - | - | - | - |
|
||||||
|
| Brace _matching_ | ✔ | ✔ | ✔ | - | - | ✔ | - |
|
||||||
|
| Brace _expansion_ | ✔ | ✔ | - | - | - | ✔ | - |
|
||||||
|
| Extglobs | partial | ✔ | ✔ | - | ✔ | - | - |
|
||||||
|
| Posix brackets | - | ✔ | ✔ | - | - | - | ✔ |
|
||||||
|
| Regular expression syntax | - | ✔ | ✔ | ✔ | ✔ | - | ✔ |
|
||||||
|
| File system operations | - | - | - | - | - | - | - |
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Performance comparison of picomatch and minimatch.
|
||||||
|
|
||||||
|
```
|
||||||
|
# .makeRe star
|
||||||
|
picomatch x 1,993,050 ops/sec ±0.51% (91 runs sampled)
|
||||||
|
minimatch x 627,206 ops/sec ±1.96% (87 runs sampled))
|
||||||
|
|
||||||
|
# .makeRe star; dot=true
|
||||||
|
picomatch x 1,436,640 ops/sec ±0.62% (91 runs sampled)
|
||||||
|
minimatch x 525,876 ops/sec ±0.60% (88 runs sampled)
|
||||||
|
|
||||||
|
# .makeRe globstar
|
||||||
|
picomatch x 1,592,742 ops/sec ±0.42% (90 runs sampled)
|
||||||
|
minimatch x 962,043 ops/sec ±1.76% (91 runs sampled)d)
|
||||||
|
|
||||||
|
# .makeRe globstars
|
||||||
|
picomatch x 1,615,199 ops/sec ±0.35% (94 runs sampled)
|
||||||
|
minimatch x 477,179 ops/sec ±1.33% (91 runs sampled)
|
||||||
|
|
||||||
|
# .makeRe with leading star
|
||||||
|
picomatch x 1,220,856 ops/sec ±0.40% (92 runs sampled)
|
||||||
|
minimatch x 453,564 ops/sec ±1.43% (94 runs sampled)
|
||||||
|
|
||||||
|
# .makeRe - basic braces
|
||||||
|
picomatch x 392,067 ops/sec ±0.70% (90 runs sampled)
|
||||||
|
minimatch x 99,532 ops/sec ±2.03% (87 runs sampled))
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Philosophies
|
||||||
|
|
||||||
|
The goal of this library is to be blazing fast, without compromising on accuracy.
|
||||||
|
|
||||||
|
**Accuracy**
|
||||||
|
|
||||||
|
The number one of goal of this library is accuracy. However, it's not unusual for different glob implementations to have different rules for matching behavior, even with simple wildcard matching. It gets increasingly more complicated when combinations of different features are combined, like when extglobs are combined with globstars, braces, slashes, and so on: `!(**/{a,b,*/c})`.
|
||||||
|
|
||||||
|
Thus, given that there is no canonical glob specification to use as a single source of truth when differences of opinion arise regarding behavior, sometimes we have to implement our best judgement and rely on feedback from users to make improvements.
|
||||||
|
|
||||||
|
**Performance**
|
||||||
|
|
||||||
|
Although this library performs well in benchmarks, and in most cases it's faster than other popular libraries we benchmarked against, we will always choose accuracy over performance. It's not helpful to anyone if our library is faster at returning the wrong answer.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Contributing</strong></summary>
|
||||||
|
|
||||||
|
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
|
||||||
|
|
||||||
|
Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Running Tests</strong></summary>
|
||||||
|
|
||||||
|
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install && npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Building docs</strong></summary>
|
||||||
|
|
||||||
|
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
|
||||||
|
|
||||||
|
To generate the readme, run the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install -g verbose/verb#dev verb-generate-readme && verb
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Author
|
||||||
|
|
||||||
|
**Jon Schlinkert**
|
||||||
|
|
||||||
|
* [GitHub Profile](https://github.com/jonschlinkert)
|
||||||
|
* [Twitter Profile](https://twitter.com/jonschlinkert)
|
||||||
|
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
Copyright © 2017-present, [Jon Schlinkert](https://github.com/jonschlinkert).
|
||||||
|
Released under the [MIT License](LICENSE).
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/** @type {import('./Reflect.getPrototypeOf')} */
|
||||||
|
module.exports = (typeof Reflect !== 'undefined' && Reflect.getPrototypeOf) || null;
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v1.1.0](https://github.com/es-shims/math-intrinsics/compare/v1.0.0...v1.1.0) - 2024-12-18
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [New] add `round` [`7cfb044`](https://github.com/es-shims/math-intrinsics/commit/7cfb04460c0fbdf1ca101eecbac3f59d11994130)
|
||||||
|
- [Tests] add attw [`e96be8f`](https://github.com/es-shims/math-intrinsics/commit/e96be8fbf58449eafe976446a0470e6ea561ad8d)
|
||||||
|
- [Dev Deps] update `@types/tape` [`30d0023`](https://github.com/es-shims/math-intrinsics/commit/30d00234ce8a3fa0094a61cd55d6686eb91e36ec)
|
||||||
|
|
||||||
|
## v1.0.0 - 2024-12-11
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- Initial implementation, tests, readme, types [`b898caa`](https://github.com/es-shims/math-intrinsics/commit/b898caae94e9994a94a42b8740f7bbcfd0a868fe)
|
||||||
|
- Initial commit [`02745b0`](https://github.com/es-shims/math-intrinsics/commit/02745b03a62255af8a332771987b55d127538d9c)
|
||||||
|
- [New] add `constants/maxArrayLength`, `mod` [`b978178`](https://github.com/es-shims/math-intrinsics/commit/b978178a57685bd23ed1c7efe2137f3784f5fcc5)
|
||||||
|
- npm init [`a39fc57`](https://github.com/es-shims/math-intrinsics/commit/a39fc57e5639a645d0bd52a0dc56202480223be2)
|
||||||
|
- Only apps should have lockfiles [`9451580`](https://github.com/es-shims/math-intrinsics/commit/94515800fb34db4f3cc7e99290042d45609ac7bd)
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const compareBuild = require('./compare-build')
|
||||||
|
const rsort = (list, loose) => list.sort((a, b) => compareBuild(b, a, loose))
|
||||||
|
module.exports = rsort
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var GetIntrinsic = require('get-intrinsic');
|
||||||
|
var callBound = require('call-bound');
|
||||||
|
var inspect = require('object-inspect');
|
||||||
|
|
||||||
|
var $TypeError = require('es-errors/type');
|
||||||
|
var $Map = GetIntrinsic('%Map%', true);
|
||||||
|
|
||||||
|
/** @type {<K, V>(thisArg: Map<K, V>, key: K) => V} */
|
||||||
|
var $mapGet = callBound('Map.prototype.get', true);
|
||||||
|
/** @type {<K, V>(thisArg: Map<K, V>, key: K, value: V) => void} */
|
||||||
|
var $mapSet = callBound('Map.prototype.set', true);
|
||||||
|
/** @type {<K, V>(thisArg: Map<K, V>, key: K) => boolean} */
|
||||||
|
var $mapHas = callBound('Map.prototype.has', true);
|
||||||
|
/** @type {<K, V>(thisArg: Map<K, V>, key: K) => boolean} */
|
||||||
|
var $mapDelete = callBound('Map.prototype.delete', true);
|
||||||
|
/** @type {<K, V>(thisArg: Map<K, V>) => number} */
|
||||||
|
var $mapSize = callBound('Map.prototype.size', true);
|
||||||
|
|
||||||
|
/** @type {import('.')} */
|
||||||
|
module.exports = !!$Map && /** @type {Exclude<import('.'), false>} */ function getSideChannelMap() {
|
||||||
|
/** @typedef {ReturnType<typeof getSideChannelMap>} Channel */
|
||||||
|
/** @typedef {Parameters<Channel['get']>[0]} K */
|
||||||
|
/** @typedef {Parameters<Channel['set']>[1]} V */
|
||||||
|
|
||||||
|
/** @type {Map<K, V> | undefined} */ var $m;
|
||||||
|
|
||||||
|
/** @type {Channel} */
|
||||||
|
var channel = {
|
||||||
|
assert: function (key) {
|
||||||
|
if (!channel.has(key)) {
|
||||||
|
throw new $TypeError('Side channel does not contain ' + inspect(key));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'delete': function (key) {
|
||||||
|
if ($m) {
|
||||||
|
var result = $mapDelete($m, key);
|
||||||
|
if ($mapSize($m) === 0) {
|
||||||
|
$m = void undefined;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
get: function (key) { // eslint-disable-line consistent-return
|
||||||
|
if ($m) {
|
||||||
|
return $mapGet($m, key);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
has: function (key) {
|
||||||
|
if ($m) {
|
||||||
|
return $mapHas($m, key);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
set: function (key, value) {
|
||||||
|
if (!$m) {
|
||||||
|
// @ts-expect-error TS can't handle narrowing a variable inside a closure
|
||||||
|
$m = new $Map();
|
||||||
|
}
|
||||||
|
$mapSet($m, key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error TODO: figure out why TS is erroring here
|
||||||
|
return channel;
|
||||||
|
};
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
declare namespace getSideChannelWeakMap {
|
||||||
|
type Channel<K, V> = {
|
||||||
|
assert: (key: K) => void;
|
||||||
|
has: (key: K) => boolean;
|
||||||
|
get: (key: K) => V | undefined;
|
||||||
|
set: (key: K, value: V) => void;
|
||||||
|
delete: (key: K) => boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function getSideChannelWeakMap<K, V>(): getSideChannelWeakMap.Channel<K, V>;
|
||||||
|
|
||||||
|
declare const x: false | typeof getSideChannelWeakMap;
|
||||||
|
|
||||||
|
export = x;
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
declare namespace getRawBody {
|
||||||
|
export type Encoding = string | true;
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
/**
|
||||||
|
* The expected length of the stream.
|
||||||
|
*/
|
||||||
|
length?: number | string | null;
|
||||||
|
/**
|
||||||
|
* The byte limit of the body. This is the number of bytes or any string
|
||||||
|
* format supported by `bytes`, for example `1000`, `'500kb'` or `'3mb'`.
|
||||||
|
*/
|
||||||
|
limit?: number | string | null;
|
||||||
|
/**
|
||||||
|
* The encoding to use to decode the body into a string. By default, a
|
||||||
|
* `Buffer` instance will be returned when no encoding is specified. Most
|
||||||
|
* likely, you want `utf-8`, so setting encoding to `true` will decode as
|
||||||
|
* `utf-8`. You can use any type of encoding supported by `iconv-lite`.
|
||||||
|
*/
|
||||||
|
encoding?: Encoding | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawBodyError extends Error {
|
||||||
|
/**
|
||||||
|
* The limit in bytes.
|
||||||
|
*/
|
||||||
|
limit?: number;
|
||||||
|
/**
|
||||||
|
* The expected length of the stream.
|
||||||
|
*/
|
||||||
|
length?: number;
|
||||||
|
expected?: number;
|
||||||
|
/**
|
||||||
|
* The received bytes.
|
||||||
|
*/
|
||||||
|
received?: number;
|
||||||
|
/**
|
||||||
|
* The encoding.
|
||||||
|
*/
|
||||||
|
encoding?: string;
|
||||||
|
/**
|
||||||
|
* The corresponding status code for the error.
|
||||||
|
*/
|
||||||
|
status: number;
|
||||||
|
statusCode: number;
|
||||||
|
/**
|
||||||
|
* The error type.
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the entire buffer of a stream either as a `Buffer` or a string.
|
||||||
|
* Validates the stream's length against an expected length and maximum
|
||||||
|
* limit. Ideal for parsing request bodies.
|
||||||
|
*/
|
||||||
|
declare function getRawBody(
|
||||||
|
stream: NodeJS.ReadableStream,
|
||||||
|
callback: (err: getRawBody.RawBodyError, body: Buffer) => void
|
||||||
|
): void;
|
||||||
|
|
||||||
|
declare function getRawBody(
|
||||||
|
stream: NodeJS.ReadableStream,
|
||||||
|
options: (getRawBody.Options & { encoding: getRawBody.Encoding }) | getRawBody.Encoding,
|
||||||
|
callback: (err: getRawBody.RawBodyError, body: string) => void
|
||||||
|
): void;
|
||||||
|
|
||||||
|
declare function getRawBody(
|
||||||
|
stream: NodeJS.ReadableStream,
|
||||||
|
options: getRawBody.Options,
|
||||||
|
callback: (err: getRawBody.RawBodyError, body: Buffer) => void
|
||||||
|
): void;
|
||||||
|
|
||||||
|
declare function getRawBody(
|
||||||
|
stream: NodeJS.ReadableStream,
|
||||||
|
options: (getRawBody.Options & { encoding: getRawBody.Encoding }) | getRawBody.Encoding
|
||||||
|
): Promise<string>;
|
||||||
|
|
||||||
|
declare function getRawBody(
|
||||||
|
stream: NodeJS.ReadableStream,
|
||||||
|
options?: getRawBody.Options
|
||||||
|
): Promise<Buffer>;
|
||||||
|
|
||||||
|
export = getRawBody;
|
||||||
@ -0,0 +1,292 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* This is the common logic for both the Node.js and web browser
|
||||||
|
* implementations of `debug()`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function setup(env) {
|
||||||
|
createDebug.debug = createDebug;
|
||||||
|
createDebug.default = createDebug;
|
||||||
|
createDebug.coerce = coerce;
|
||||||
|
createDebug.disable = disable;
|
||||||
|
createDebug.enable = enable;
|
||||||
|
createDebug.enabled = enabled;
|
||||||
|
createDebug.humanize = require('ms');
|
||||||
|
createDebug.destroy = destroy;
|
||||||
|
|
||||||
|
Object.keys(env).forEach(key => {
|
||||||
|
createDebug[key] = env[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently active debug mode names, and names to skip.
|
||||||
|
*/
|
||||||
|
|
||||||
|
createDebug.names = [];
|
||||||
|
createDebug.skips = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of special "%n" handling functions, for the debug "format" argument.
|
||||||
|
*
|
||||||
|
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
|
||||||
|
*/
|
||||||
|
createDebug.formatters = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects a color for a debug namespace
|
||||||
|
* @param {String} namespace The namespace string for the debug instance to be colored
|
||||||
|
* @return {Number|String} An ANSI color code for the given namespace
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
function selectColor(namespace) {
|
||||||
|
let hash = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < namespace.length; i++) {
|
||||||
|
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
|
||||||
|
hash |= 0; // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
|
||||||
|
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
||||||
|
}
|
||||||
|
createDebug.selectColor = selectColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a debugger with the given `namespace`.
|
||||||
|
*
|
||||||
|
* @param {String} namespace
|
||||||
|
* @return {Function}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
function createDebug(namespace) {
|
||||||
|
let prevTime;
|
||||||
|
let enableOverride = null;
|
||||||
|
let namespacesCache;
|
||||||
|
let enabledCache;
|
||||||
|
|
||||||
|
function debug(...args) {
|
||||||
|
// Disabled?
|
||||||
|
if (!debug.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const self = debug;
|
||||||
|
|
||||||
|
// Set `diff` timestamp
|
||||||
|
const curr = Number(new Date());
|
||||||
|
const ms = curr - (prevTime || curr);
|
||||||
|
self.diff = ms;
|
||||||
|
self.prev = prevTime;
|
||||||
|
self.curr = curr;
|
||||||
|
prevTime = curr;
|
||||||
|
|
||||||
|
args[0] = createDebug.coerce(args[0]);
|
||||||
|
|
||||||
|
if (typeof args[0] !== 'string') {
|
||||||
|
// Anything else let's inspect with %O
|
||||||
|
args.unshift('%O');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply any `formatters` transformations
|
||||||
|
let index = 0;
|
||||||
|
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
|
||||||
|
// If we encounter an escaped % then don't increase the array index
|
||||||
|
if (match === '%%') {
|
||||||
|
return '%';
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
const formatter = createDebug.formatters[format];
|
||||||
|
if (typeof formatter === 'function') {
|
||||||
|
const val = args[index];
|
||||||
|
match = formatter.call(self, val);
|
||||||
|
|
||||||
|
// Now we need to remove `args[index]` since it's inlined in the `format`
|
||||||
|
args.splice(index, 1);
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply env-specific formatting (colors, etc.)
|
||||||
|
createDebug.formatArgs.call(self, args);
|
||||||
|
|
||||||
|
const logFn = self.log || createDebug.log;
|
||||||
|
logFn.apply(self, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.namespace = namespace;
|
||||||
|
debug.useColors = createDebug.useColors();
|
||||||
|
debug.color = createDebug.selectColor(namespace);
|
||||||
|
debug.extend = extend;
|
||||||
|
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
|
||||||
|
|
||||||
|
Object.defineProperty(debug, 'enabled', {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
get: () => {
|
||||||
|
if (enableOverride !== null) {
|
||||||
|
return enableOverride;
|
||||||
|
}
|
||||||
|
if (namespacesCache !== createDebug.namespaces) {
|
||||||
|
namespacesCache = createDebug.namespaces;
|
||||||
|
enabledCache = createDebug.enabled(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return enabledCache;
|
||||||
|
},
|
||||||
|
set: v => {
|
||||||
|
enableOverride = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Env-specific initialization logic for debug instances
|
||||||
|
if (typeof createDebug.init === 'function') {
|
||||||
|
createDebug.init(debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extend(namespace, delimiter) {
|
||||||
|
const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
|
||||||
|
newDebug.log = this.log;
|
||||||
|
return newDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables a debug mode by namespaces. This can include modes
|
||||||
|
* separated by a colon and wildcards.
|
||||||
|
*
|
||||||
|
* @param {String} namespaces
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
function enable(namespaces) {
|
||||||
|
createDebug.save(namespaces);
|
||||||
|
createDebug.namespaces = namespaces;
|
||||||
|
|
||||||
|
createDebug.names = [];
|
||||||
|
createDebug.skips = [];
|
||||||
|
|
||||||
|
const split = (typeof namespaces === 'string' ? namespaces : '')
|
||||||
|
.trim()
|
||||||
|
.replace(/\s+/g, ',')
|
||||||
|
.split(',')
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
for (const ns of split) {
|
||||||
|
if (ns[0] === '-') {
|
||||||
|
createDebug.skips.push(ns.slice(1));
|
||||||
|
} else {
|
||||||
|
createDebug.names.push(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given string matches a namespace template, honoring
|
||||||
|
* asterisks as wildcards.
|
||||||
|
*
|
||||||
|
* @param {String} search
|
||||||
|
* @param {String} template
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
function matchesTemplate(search, template) {
|
||||||
|
let searchIndex = 0;
|
||||||
|
let templateIndex = 0;
|
||||||
|
let starIndex = -1;
|
||||||
|
let matchIndex = 0;
|
||||||
|
|
||||||
|
while (searchIndex < search.length) {
|
||||||
|
if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
|
||||||
|
// Match character or proceed with wildcard
|
||||||
|
if (template[templateIndex] === '*') {
|
||||||
|
starIndex = templateIndex;
|
||||||
|
matchIndex = searchIndex;
|
||||||
|
templateIndex++; // Skip the '*'
|
||||||
|
} else {
|
||||||
|
searchIndex++;
|
||||||
|
templateIndex++;
|
||||||
|
}
|
||||||
|
} else if (starIndex !== -1) { // eslint-disable-line no-negated-condition
|
||||||
|
// Backtrack to the last '*' and try to match more characters
|
||||||
|
templateIndex = starIndex + 1;
|
||||||
|
matchIndex++;
|
||||||
|
searchIndex = matchIndex;
|
||||||
|
} else {
|
||||||
|
return false; // No match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle trailing '*' in template
|
||||||
|
while (templateIndex < template.length && template[templateIndex] === '*') {
|
||||||
|
templateIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return templateIndex === template.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable debug output.
|
||||||
|
*
|
||||||
|
* @return {String} namespaces
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
function disable() {
|
||||||
|
const namespaces = [
|
||||||
|
...createDebug.names,
|
||||||
|
...createDebug.skips.map(namespace => '-' + namespace)
|
||||||
|
].join(',');
|
||||||
|
createDebug.enable('');
|
||||||
|
return namespaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given mode name is enabled, false otherwise.
|
||||||
|
*
|
||||||
|
* @param {String} name
|
||||||
|
* @return {Boolean}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
function enabled(name) {
|
||||||
|
for (const skip of createDebug.skips) {
|
||||||
|
if (matchesTemplate(name, skip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ns of createDebug.names) {
|
||||||
|
if (matchesTemplate(name, ns)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coerce `val`.
|
||||||
|
*
|
||||||
|
* @param {Mixed} val
|
||||||
|
* @return {Mixed}
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
function coerce(val) {
|
||||||
|
if (val instanceof Error) {
|
||||||
|
return val.stack || val.message;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XXX DO NOT USE. This is a temporary stub function.
|
||||||
|
* XXX It WILL be removed in the next major release.
|
||||||
|
*/
|
||||||
|
function destroy() {
|
||||||
|
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||||
|
}
|
||||||
|
|
||||||
|
createDebug.enable(createDebug.load());
|
||||||
|
|
||||||
|
return createDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = setup;
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const homeDirectory = os.homedir();
|
||||||
|
const configDir =
|
||||||
|
process.env.XDG_CONFIG_HOME ||
|
||||||
|
path.join(homeDirectory, '.config', 'simple-update-notifier');
|
||||||
|
|
||||||
|
const getConfigFile = (packageName: string) => {
|
||||||
|
return path.join(
|
||||||
|
configDir,
|
||||||
|
`${packageName.replace('@', '').replace('/', '__')}.json`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createConfigDir = () => {
|
||||||
|
if (!fs.existsSync(configDir)) {
|
||||||
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLastUpdate = (packageName: string) => {
|
||||||
|
const configFile = getConfigFile(packageName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(configFile)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const file = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
||||||
|
return file.lastUpdateCheck as number;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveLastUpdate = (packageName: string) => {
|
||||||
|
const configFile = getConfigFile(packageName);
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
configFile,
|
||||||
|
JSON.stringify({ lastUpdateCheck: new Date().getTime() })
|
||||||
|
);
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user