Added vscodium

This commit is contained in:
Kristofers Solo
2022-04-28 21:17:01 +03:00
parent 837a479d82
commit d7dddc39ef
29139 changed files with 250215 additions and 45125 deletions

View File

@@ -0,0 +1,21 @@
# Ignore built files
obj
*.tgz
# Ignore Visual Studio files
*.suo
*.sln
*.njsproj
.vscode/
# Ignore test files
Tests
# Don't publish source files that aren't needed in package
*.ts
*.js.map
*.d.ts
.travis.yml
.ntvs_analysis.dat
Declarations

View File

@@ -0,0 +1,116 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var url = require("url");
var ContractsModule = require("../Library/Contracts");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var RequestParser = require("./RequestParser");
/**
* Helper class to read data from the requst/response objects and convert them into the telemetry contract
*/
var ClientRequestParser = (function (_super) {
__extends(ClientRequestParser, _super);
function ClientRequestParser(requestOptions, request) {
_super.call(this);
if (request && request.method && requestOptions) {
// The ClientRequest.method property isn't documented, but is always there.
this.method = request.method;
this.url = ClientRequestParser._getUrlFromRequestOptions(requestOptions, request);
this.startTime = +new Date();
}
}
/**
* Called when the ClientRequest emits an error event.
*/
ClientRequestParser.prototype.onError = function (error, properties) {
this._setStatus(undefined, error, properties);
};
/**
* Called when the ClientRequest emits a response event.
*/
ClientRequestParser.prototype.onResponse = function (response, properties) {
this._setStatus(response.statusCode, undefined, properties);
this.targetIKeyHash =
response.headers && response.headers[RequestResponseHeaders.targetInstrumentationKeyHeader];
};
/**
* Gets a dependency data contract object for a completed ClientRequest.
*/
ClientRequestParser.prototype.getDependencyData = function () {
var urlObject = url.parse(this.url);
urlObject.search = undefined;
urlObject.hash = undefined;
var dependencyName = this.method.toUpperCase() + " " + urlObject.pathname;
var remoteDependency = new ContractsModule.Contracts.RemoteDependencyData();
remoteDependency.type = ContractsModule.Contracts.RemoteDependencyDataConstants.TYPE_HTTP;
if (this.targetIKeyHash) {
remoteDependency.type = "ApplicationInsights";
remoteDependency.target = urlObject.hostname + " | " + this.targetIKeyHash;
}
else {
remoteDependency.type = ContractsModule.Contracts.RemoteDependencyDataConstants.TYPE_HTTP;
remoteDependency.target = urlObject.hostname;
}
remoteDependency.name = dependencyName;
remoteDependency.data = this.url;
remoteDependency.duration = Util.msToTimeSpan(this.duration);
remoteDependency.success = this._isSuccess();
remoteDependency.resultCode = this.statusCode ? this.statusCode.toString() : null;
remoteDependency.properties = this.properties || {};
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.RemoteDependencyData";
data.baseData = remoteDependency;
return data;
};
/**
* Builds a URL from request options, using the same logic as http.request(). This is
* necessary because a ClientRequest object does not expose a url property.
*/
ClientRequestParser._getUrlFromRequestOptions = function (options, request) {
if (typeof options === 'string') {
options = url.parse(options);
}
else {
// Avoid modifying the original options object.
var originalOptions_1 = options;
options = {};
if (originalOptions_1) {
Object.keys(originalOptions_1).forEach(function (key) {
options[key] = originalOptions_1[key];
});
}
}
// Oddly, url.format ignores path and only uses pathname and search,
// so create them from the path, if path was specified
if (options.path) {
var parsedQuery = url.parse(options.path);
options.pathname = parsedQuery.pathname;
options.search = parsedQuery.search;
}
// Simiarly, url.format ignores hostname and port if host is specified,
// even if host doesn't have the port, but http.request does not work
// this way. It will use the port if one is not specified in host,
// effectively treating host as hostname, but will use the port specified
// in host if it exists.
if (options.host && options.port) {
// Force a protocol so it will parse the host as the host, not path.
// It is discarded and not used, so it doesn't matter if it doesn't match
var parsedHost = url.parse("http://" + options.host);
if (!parsedHost.port && options.port) {
options.hostname = options.host;
delete options.host;
}
}
// Mix in default values used by http.request and others
options.protocol = options.protocol || request.agent.protocol;
options.hostname = options.hostname || 'localhost';
return url.format(options);
};
return ClientRequestParser;
}(RequestParser));
module.exports = ClientRequestParser;

View File

@@ -0,0 +1,100 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var http = require("http");
var https = require("https");
var Logging = require("../Library/Logging");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var ClientRequestParser = require("./ClientRequestParser");
var AutoCollectClientRequests = (function () {
function AutoCollectClientRequests(client) {
if (!!AutoCollectClientRequests.INSTANCE) {
throw new Error("Client request tracking should be configured from the applicationInsights object");
}
AutoCollectClientRequests.INSTANCE = this;
this._client = client;
}
AutoCollectClientRequests.prototype.enable = function (isEnabled) {
this._isEnabled = isEnabled;
if (this._isEnabled && !this._isInitialized) {
this._initialize();
}
};
AutoCollectClientRequests.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectClientRequests.prototype._initialize = function () {
var _this = this;
this._isInitialized = true;
var originalRequest = http.request;
http.request = function (options) {
var requestArgs = [];
for (var _i = 1; _i < arguments.length; _i++) {
requestArgs[_i - 1] = arguments[_i];
}
var request = originalRequest.call.apply(originalRequest, [http, options].concat(requestArgs));
if (request && options && !options[AutoCollectClientRequests.disableCollectionRequestOption]) {
AutoCollectClientRequests.trackRequest(_this._client, options, request);
}
return request;
};
// On node >= v0.11.12, https.request just calls http.request (with additional options).
// But on older versions, https.request needs to be patched also.
// The regex matches versions < 0.11.12 (avoiding a semver package dependency).
if (/^0\.([0-9]\.)|(10\.)|(11\.([0-9]|10|11)$)/.test(process.versions.node)) {
var originalHttpsRequest_1 = https.request;
https.request = function (options) {
var requestArgs = [];
for (var _i = 1; _i < arguments.length; _i++) {
requestArgs[_i - 1] = arguments[_i];
}
var request = originalHttpsRequest_1.call.apply(originalHttpsRequest_1, [https, options].concat(requestArgs));
if (request && options && !options[AutoCollectClientRequests.disableCollectionRequestOption]) {
AutoCollectClientRequests.trackRequest(_this._client, options, request);
}
return request;
};
}
};
/**
* Tracks an outgoing request. Because it may set headers this method must be called before
* writing content to or ending the request.
*/
AutoCollectClientRequests.trackRequest = function (client, requestOptions, request, properties) {
if (!requestOptions || !request || !client) {
Logging.info("AutoCollectClientRequests.trackRequest was called with invalid parameters: ", !requestOptions, !request, !client);
return;
}
var requestParser = new ClientRequestParser(requestOptions, request);
// Add the source ikey hash to the request headers, if a value was not already provided.
// The getHeader/setHeader methods aren't available on very old Node versions, and
// are not included in the v0.10 type declarations currently used. So check if the
// methods exist before invoking them.
if (client.config && client.config.instrumentationKeyHash &&
Util.canIncludeCorrelationHeader(client, requestParser.getUrl()) &&
request['getHeader'] && request['setHeader'] &&
!request['getHeader'](RequestResponseHeaders.sourceInstrumentationKeyHeader)) {
request['setHeader'](RequestResponseHeaders.sourceInstrumentationKeyHeader, client.config.instrumentationKeyHash);
}
// Collect dependency telemetry about the request when it finishes.
if (request.on) {
request.on('response', function (response) {
requestParser.onResponse(response, properties);
var context = { "http.RequestOptions": requestOptions, "http.ClientRequest": request, "http.ClientResponse": response };
client.track(requestParser.getDependencyData(), null, context);
});
request.on('error', function (e) {
requestParser.onError(e, properties);
var context = { "http.RequestOptions": requestOptions, "http.ClientRequest": request, "Error": e };
client.track(requestParser.getDependencyData(), null, context);
});
}
};
AutoCollectClientRequests.prototype.dispose = function () {
AutoCollectClientRequests.INSTANCE = null;
this._isInitialized = false;
};
AutoCollectClientRequests.disableCollectionRequestOption = 'disableAppInsightsAutoCollection';
return AutoCollectClientRequests;
}());
module.exports = AutoCollectClientRequests;

View File

@@ -0,0 +1,22 @@
"use strict";
var AutoCollectConsole = (function () {
function AutoCollectConsole(client) {
if (!!AutoCollectConsole.INSTANCE) {
throw new Error("Console logging adapter tracking should be configured from the applicationInsights object");
}
this._client = client;
AutoCollectConsole.INSTANCE = this;
}
AutoCollectConsole.prototype.enable = function (isEnabled) {
// todo: investigate feasibility/utility of this; does it make sense to have a logging adapter in node?
};
AutoCollectConsole.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectConsole.prototype.dispose = function () {
AutoCollectConsole.INSTANCE = null;
};
AutoCollectConsole._methodNames = ["debug", "info", "log", "warn", "error"];
return AutoCollectConsole;
}());
module.exports = AutoCollectConsole;

View File

@@ -0,0 +1,191 @@
"use strict";
var Util = require("../Library/Util");
var CorrelationContextManager = (function () {
function CorrelationContextManager() {
}
/**
* Provides the current Context.
* The context is the most recent one entered into for the current
* logical chain of execution, including across asynchronous calls.
*/
CorrelationContextManager.getCurrentContext = function () {
if (!CorrelationContextManager.enabled) {
return null;
}
return Zone.current.get("context");
};
/**
* A helper to generate objects conforming to the CorrelationContext interface
*/
CorrelationContextManager.generateContextObject = function (parentId, operationName, operationId) {
operationId = operationId || Util.newGuid();
parentId = parentId || operationId;
if (this.enabled) {
return {
operation: {
name: operationName,
id: operationId,
parentId: parentId
},
customProperties: {}
};
}
return null;
};
/**
* Runs a function inside a given Context.
* All logical children of the execution path that entered this Context
* will receive this Context object on calls to GetCurrentContext.
*/
CorrelationContextManager.runWithContext = function (context, fn) {
if (CorrelationContextManager.enabled) {
var newZone = Zone.current.fork({
name: "AI-" + ((context && context.operation.parentId) || "Unknown"),
properties: { context: context }
});
newZone.run(fn);
}
else {
fn();
}
};
/**
* Patches a callback to restore the correct Context when getCurrentContext
* is run within it. This is necessary if automatic correlation fails to work
* with user-included libraries.
*
* The supplied callback will be given the same context that was present for
* the call to wrapCallback. */
CorrelationContextManager.wrapCallback = function (fn) {
if (CorrelationContextManager.enabled) {
return Zone.current.wrap(fn, "User-wrapped method");
}
return fn;
};
/**
* Enables the CorrelationContextManager.
*/
CorrelationContextManager.enable = function () {
if (!this.isNodeVersionCompatible()) {
this.enabled = false;
return;
}
// Load in Zone.js
require("zone.js");
// Run patches for Zone.js
if (!this.hasEverEnabled) {
this.hasEverEnabled = true;
this.patchError();
this.patchTimers(["setTimeout", "setInterval"]);
this.patchRedis();
}
this.enabled = true;
};
/**
* Disables the CorrelationContextManager.
*/
CorrelationContextManager.disable = function () {
this.enabled = false;
};
/**
* Reports if the CorrelationContextManager is able to run in this environment
*/
CorrelationContextManager.isNodeVersionCompatible = function () {
// Unit tests warn of errors < 3.3 from timer patching. All versions before 4 were 0.x
var nodeVer = process.versions.node.split(".");
return parseInt(nodeVer[0]) > 3 || (parseInt(nodeVer[0]) > 2 && parseInt(nodeVer[1]) > 2);
};
// Patch methods that manually go async that Zone doesn't catch
CorrelationContextManager.requireForPatch = function (module) {
var req = null;
try {
req = require(module);
}
catch (e) {
return null;
}
return req;
};
// A good example of patching a third party library to respect context.
// send_command is always used in this library to send data out.
// By overwriting the function to capture the callback provided to it,
// and wrapping that callback, we ensure that consumers of this library
// will have context persisted.
CorrelationContextManager.patchRedis = function () {
var redis = this.requireForPatch("redis");
if (redis && redis.RedisClient) {
var orig = redis.RedisClient.prototype.send_command;
redis.RedisClient.prototype.send_command = function () {
var args = Array.prototype.slice.call(arguments);
var lastArg = args[args.length - 1];
if (typeof lastArg === "function") {
args[args.length - 1] = Zone.current.wrap(lastArg, "AI.CCM.patchRedis");
}
else if (lastArg instanceof Array && typeof lastArg[lastArg.length - 1] === "function") {
// The last argument can be an array!
var lastIndexLastArg = lastArg[lastArg.length - 1];
lastArg[lastArg.length - 1] = Zone.current.wrap(lastIndexLastArg, "AI.CCM.patchRedis");
}
return orig.apply(this, args);
};
}
};
// Zone.js breaks concatenation of timer return values.
// This fixes that.
CorrelationContextManager.patchTimers = function (methodNames) {
methodNames.forEach(function (methodName) {
var orig = global[methodName];
global[methodName] = function () {
var ret = orig.apply(this, arguments);
ret.toString = function () {
if (this.data && typeof this.data.handleId !== 'undefined') {
return this.data.handleId.toString();
}
else {
return Object.prototype.toString.call(this);
}
};
return ret;
};
});
};
// Zone.js breaks deepEqual on error objects (by making internal properties enumerable).
// This fixes that by subclassing the error object and making all properties not enumerable
CorrelationContextManager.patchError = function () {
var orig = global.Error;
// New error handler
function AppInsightsAsyncCorrelatedErrorWrapper() {
if (!(this instanceof AppInsightsAsyncCorrelatedErrorWrapper)) {
return AppInsightsAsyncCorrelatedErrorWrapper.apply(Object.create(AppInsightsAsyncCorrelatedErrorWrapper.prototype), arguments);
}
orig.apply(this, arguments);
// getOwnPropertyNames should be a superset of Object.keys...
// This appears to not always be the case
var props = Object.getOwnPropertyNames(this).concat(Object.keys(this));
// Zone.js will automatically create some hidden properties at read time.
// We need to proactively make those not enumerable as well as the currently visible properties
for (var i = 0; i < props.length; i++) {
var propertyName = props[i];
var hiddenPropertyName = Zone['__symbol__'](propertyName);
Object.defineProperty(this, propertyName, { enumerable: false });
Object.defineProperty(this, hiddenPropertyName, { enumerable: false, writable: true });
}
return this;
}
// Inherit from the Zone.js error handler
AppInsightsAsyncCorrelatedErrorWrapper.prototype = orig.prototype;
// We need this loop to copy outer methods like Error.captureStackTrace
var props = Object.getOwnPropertyNames(orig);
for (var i = 0; i < props.length; i++) {
var propertyName = props[i];
if (!AppInsightsAsyncCorrelatedErrorWrapper[propertyName]) {
Object.defineProperty(AppInsightsAsyncCorrelatedErrorWrapper, propertyName, Object.getOwnPropertyDescriptor(orig, propertyName));
}
}
global.Error = AppInsightsAsyncCorrelatedErrorWrapper;
};
CorrelationContextManager.enabled = false;
CorrelationContextManager.hasEverEnabled = false;
return CorrelationContextManager;
}());
exports.CorrelationContextManager = CorrelationContextManager;

View File

@@ -0,0 +1,150 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var ContractsModule = require("../Library/Contracts");
var Util = require("../Library/Util");
var AutoCollectExceptions = (function () {
function AutoCollectExceptions(client) {
if (!!AutoCollectExceptions.INSTANCE) {
throw new Error("Exception tracking should be configured from the applicationInsights object");
}
AutoCollectExceptions.INSTANCE = this;
this._client = client;
}
AutoCollectExceptions.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectExceptions.prototype.enable = function (isEnabled) {
var _this = this;
if (isEnabled) {
this._isInitialized = true;
var self = this;
if (!this._exceptionListenerHandle) {
var handle = function (reThrow, error) {
var data = AutoCollectExceptions.getExceptionData(error, false);
var envelope = _this._client.getEnvelope(data);
_this._client.channel.handleCrash(envelope);
if (reThrow) {
throw error;
}
};
this._exceptionListenerHandle = handle.bind(this, true);
this._rejectionListenerHandle = handle.bind(this, false);
process.on("uncaughtException", this._exceptionListenerHandle);
process.on("unhandledRejection", this._rejectionListenerHandle);
}
}
else {
if (this._exceptionListenerHandle) {
process.removeListener("uncaughtException", this._exceptionListenerHandle);
process.removeListener("unhandledRejection", this._rejectionListenerHandle);
this._exceptionListenerHandle = undefined;
this._rejectionListenerHandle = undefined;
delete this._exceptionListenerHandle;
delete this._rejectionListenerHandle;
}
}
};
/**
* Track an exception
* @param error the exception to track
* @param handledAt where this exception was handled (leave null for unhandled)
* @param properties additional properties
* @param measurements metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
AutoCollectExceptions.getExceptionData = function (error, isHandled, properties, measurements) {
var exception = new ContractsModule.Contracts.ExceptionData();
exception.properties = properties;
exception.severityLevel = ContractsModule.Contracts.SeverityLevel.Error;
exception.measurements = measurements;
exception.exceptions = [];
var stack = error["stack"];
var exceptionDetails = new ContractsModule.Contracts.ExceptionDetails();
exceptionDetails.message = error.message;
exceptionDetails.typeName = error.name;
exceptionDetails.parsedStack = this.parseStack(stack);
exceptionDetails.hasFullStack = Util.isArray(exceptionDetails.parsedStack) && exceptionDetails.parsedStack.length > 0;
exception.exceptions.push(exceptionDetails);
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.ExceptionData";
data.baseData = exception;
return data;
};
AutoCollectExceptions.parseStack = function (stack) {
var parsedStack = undefined;
if (typeof stack === "string") {
var frames = stack.split("\n");
parsedStack = [];
var level = 0;
var totalSizeInBytes = 0;
for (var i = 0; i <= frames.length; i++) {
var frame = frames[i];
if (_StackFrame.regex.test(frame)) {
var parsedFrame = new _StackFrame(frames[i], level++);
totalSizeInBytes += parsedFrame.sizeInBytes;
parsedStack.push(parsedFrame);
}
}
// DP Constraint - exception parsed stack must be < 32KB
// remove frames from the middle to meet the threshold
var exceptionParsedStackThreshold = 32 * 1024;
if (totalSizeInBytes > exceptionParsedStackThreshold) {
var left = 0;
var right = parsedStack.length - 1;
var size = 0;
var acceptedLeft = left;
var acceptedRight = right;
while (left < right) {
// check size
var lSize = parsedStack[left].sizeInBytes;
var rSize = parsedStack[right].sizeInBytes;
size += lSize + rSize;
if (size > exceptionParsedStackThreshold) {
// remove extra frames from the middle
var howMany = acceptedRight - acceptedLeft + 1;
parsedStack.splice(acceptedLeft, howMany);
break;
}
// update pointers
acceptedLeft = left;
acceptedRight = right;
left++;
right--;
}
}
}
return parsedStack;
};
AutoCollectExceptions.prototype.dispose = function () {
AutoCollectExceptions.INSTANCE = null;
this._isInitialized = false;
};
AutoCollectExceptions.INSTANCE = null;
return AutoCollectExceptions;
}());
var _StackFrame = (function () {
function _StackFrame(frame, level) {
this.sizeInBytes = 0;
this.level = level;
this.method = "<no_method>";
this.assembly = Util.trim(frame);
var matches = frame.match(_StackFrame.regex);
if (matches && matches.length >= 5) {
this.method = Util.trim(matches[2]) || this.method;
this.fileName = Util.trim(matches[4]) || "<no_filename>";
this.line = parseInt(matches[5]) || 0;
}
this.sizeInBytes += this.method.length;
this.sizeInBytes += this.fileName.length;
this.sizeInBytes += this.assembly.length;
// todo: these might need to be removed depending on how the back-end settles on their size calculation
this.sizeInBytes += _StackFrame.baseSize;
this.sizeInBytes += this.level.toString().length;
this.sizeInBytes += this.line.toString().length;
}
// regex to match stack frames from ie/chrome/ff
// methodName=$2, fileName=$4, lineNo=$5, column=$6
_StackFrame.regex = /^([\s]+at)?(.*?)(\@|\s\(|\s)([^\(\@\n]+):([0-9]+):([0-9]+)(\)?)$/;
_StackFrame.baseSize = 58; //'{"method":"","level":,"assembly":"","fileName":"","line":}'.length
return _StackFrame;
}());
module.exports = AutoCollectExceptions;

View File

@@ -0,0 +1,231 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var http = require("http");
var os = require("os");
var ContractsModule = require("../Library/Contracts");
var Logging = require("../Library/Logging");
var PerfCounterType;
(function (PerfCounterType) {
PerfCounterType[PerfCounterType["ProcessorTime"] = 0] = "ProcessorTime";
PerfCounterType[PerfCounterType["AvailableMemory"] = 1] = "AvailableMemory";
PerfCounterType[PerfCounterType["RequestsPerSec"] = 2] = "RequestsPerSec";
PerfCounterType[PerfCounterType["PrivateBytes"] = 3] = "PrivateBytes";
PerfCounterType[PerfCounterType["RequestExecutionTime"] = 4] = "RequestExecutionTime";
PerfCounterType[PerfCounterType["PercentProcessorTime"] = 5] = "PercentProcessorTime";
})(PerfCounterType || (PerfCounterType = {}));
var AutoCollectPerformance = (function () {
function AutoCollectPerformance(client) {
if (!!AutoCollectPerformance.INSTANCE) {
throw new Error("Performance tracking should be configured from the applicationInsights object");
}
AutoCollectPerformance.INSTANCE = this;
this._isInitialized = false;
this._client = client;
}
AutoCollectPerformance.prototype.enable = function (isEnabled) {
var _this = this;
this._isEnabled = isEnabled;
if (this._isEnabled && !this._isInitialized) {
this._initialize();
}
if (isEnabled) {
if (!this._handle) {
this._lastCpus = os.cpus();
this._lastRequests = {
totalRequestCount: AutoCollectPerformance._totalRequestCount,
totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount,
time: +new Date
};
this._handle = setInterval(function () { return _this.trackPerformance(); }, 10000);
}
}
else {
if (this._handle) {
clearInterval(this._handle);
this._handle = undefined;
}
}
};
AutoCollectPerformance.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectPerformance.prototype._initialize = function () {
var _this = this;
this._isInitialized = true;
var originalServer = http.createServer;
http.createServer = function (onRequest) {
return originalServer(function (request, response) {
if (_this._isEnabled) {
AutoCollectPerformance.countRequest(request, response);
}
if (typeof onRequest === "function") {
onRequest(request, response);
}
});
};
};
AutoCollectPerformance.countRequest = function (request, response) {
var _this = this;
var start = +new Date;
if (!request || !response) {
Logging.warn("AutoCollectPerformance.countRequest was called with invalid parameters: ", !!request, !!response);
return;
}
// response listeners
if (typeof response.once === "function") {
response.once("finish", function () {
var end = +new Date;
_this._lastRequestExecutionTime = end - start;
AutoCollectPerformance._totalRequestCount++;
if (response.statusCode >= 400) {
AutoCollectPerformance._totalFailedRequestCount++;
}
});
}
};
AutoCollectPerformance.prototype.trackPerformance = function () {
this._trackCpu();
this._trackMemory();
this._trackNetwork();
};
// this is necessary to accommodate some point-in-time UI quirks
AutoCollectPerformance.prototype._trackLegacyPerformance = function (counterType, value) {
var perfmetric = new ContractsModule.Contracts.PerformanceCounterData();
// semantic descriptions of these can be found here: https://support.microsoft.com/en-us/kb/815159/
switch (counterType) {
case PerfCounterType.ProcessorTime:
perfmetric.categoryName = "Process";
perfmetric.counterName = "% Processor Time";
break;
case PerfCounterType.AvailableMemory:
perfmetric.categoryName = "Memory";
perfmetric.counterName = "Available Bytes";
break;
case PerfCounterType.RequestsPerSec:
perfmetric.categoryName = "ASP.NET Applications";
perfmetric.counterName = "Requests/Sec";
break;
case PerfCounterType.PrivateBytes:
perfmetric.categoryName = "Process";
perfmetric.counterName = "Private Bytes";
break;
case PerfCounterType.RequestExecutionTime:
perfmetric.categoryName = "ASP.NET Applications";
perfmetric.counterName = "Request Execution Time";
break;
case PerfCounterType.PercentProcessorTime:
perfmetric.categoryName = "Processor";
perfmetric.counterName = "% Processor Time";
break;
}
perfmetric.count = 1;
perfmetric.kind = ContractsModule.Contracts.DataPointType.Aggregation;
perfmetric.max = value;
perfmetric.min = value;
perfmetric.stdDev = 0;
perfmetric.value = value;
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.PerformanceCounterData";
data.baseData = perfmetric;
this._client.track(data);
};
AutoCollectPerformance.prototype._trackCpu = function () {
// this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary
// to find the delta since the last measurement
var cpus = os.cpus();
if (cpus && cpus.length && this._lastCpus && cpus.length === this._lastCpus.length) {
var totalUser = 0;
var totalSys = 0;
var totalNice = 0;
var totalIdle = 0;
var totalIrq = 0;
for (var i = 0; !!cpus && i < cpus.length; i++) {
var cpu = cpus[i];
var lastCpu = this._lastCpus[i];
var name = "% cpu(" + i + ") ";
var model = cpu.model;
var speed = cpu.speed;
var times = cpu.times;
var lastTimes = lastCpu.times;
// user cpu time (or) % CPU time spent in user space
var user = (times.user - lastTimes.user) || 0;
totalUser += user;
// system cpu time (or) % CPU time spent in kernel space
var sys = (times.sys - lastTimes.sys) || 0;
totalSys += sys;
// user nice cpu time (or) % CPU time spent on low priority processes
var nice = (times.nice - lastTimes.nice) || 0;
totalNice += nice;
// idle cpu time (or) % CPU time spent idle
var idle = (times.idle - lastTimes.idle) || 0;
totalIdle += idle;
// irq (or) % CPU time spent servicing/handling hardware interrupts
var irq = (times.irq - lastTimes.irq) || 0;
totalIrq += irq;
var total = (user + sys + nice + idle + irq) || 1; // don"t let this be 0 since it is a divisor
this._client.trackMetric(name + "user", user / total);
}
var combinedName = "% total cpu ";
var combinedTotal = (totalUser + totalSys + totalNice + totalIdle + totalIrq) || 1;
this._client.trackMetric(combinedName + "user", totalUser / combinedTotal);
this._client.trackMetric(combinedName + "sys", totalSys / combinedTotal);
this._client.trackMetric(combinedName + "nice", totalNice / combinedTotal);
this._client.trackMetric(combinedName + "idle", totalIdle / combinedTotal);
this._client.trackMetric(combinedName + "irq", totalIrq / combinedTotal);
// todo: remove this legacy counter once the UI updates (~june 2015)
this._trackLegacyPerformance(PerfCounterType.ProcessorTime, totalUser / combinedTotal);
this._trackLegacyPerformance(PerfCounterType.PercentProcessorTime, (combinedTotal - totalIdle) / combinedTotal);
}
this._lastCpus = cpus;
};
AutoCollectPerformance.prototype._trackMemory = function () {
var totalMem = os.totalmem();
var freeMem = os.freemem();
var usedMem = totalMem - freeMem;
var percentUsedMem = usedMem / (totalMem || 1);
var percentAvailableMem = freeMem / (totalMem || 1);
this._client.trackMetric("Memory Used", usedMem);
this._client.trackMetric("Memory Free", freeMem);
this._client.trackMetric("Memory Total", totalMem);
this._client.trackMetric("% Memory Used", percentUsedMem);
this._client.trackMetric("% Memory Free", percentAvailableMem);
// todo: remove this legacy counter once the UI updates (~june 2015)
this._trackLegacyPerformance(PerfCounterType.AvailableMemory, freeMem);
this._trackLegacyPerformance(PerfCounterType.PrivateBytes, usedMem);
};
AutoCollectPerformance.prototype._trackNetwork = function () {
// track total request counters
var lastRequests = this._lastRequests;
var requests = {
totalRequestCount: AutoCollectPerformance._totalRequestCount,
totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount,
time: +new Date
};
var intervalRequests = (requests.totalRequestCount - lastRequests.totalRequestCount) || 0;
var intervalFailedRequests = (requests.totalFailedRequestCount - lastRequests.totalFailedRequestCount) || 0;
var elapsedMs = requests.time - lastRequests.time;
var elapsedSeconds = elapsedMs / 1000;
if (elapsedMs > 0) {
var requestsPerSec = intervalRequests / elapsedSeconds;
var failedRequestsPerSec = intervalFailedRequests / elapsedSeconds;
this._client.trackMetric("Total Requests", requests.totalRequestCount);
this._client.trackMetric("Total Failed Requests", requests.totalFailedRequestCount);
this._client.trackMetric("Requests per Second", requestsPerSec);
this._client.trackMetric("Failed Requests per Second", failedRequestsPerSec);
this._client.trackMetric("Last Request Execution Time", AutoCollectPerformance._lastRequestExecutionTime);
// todo: remove this legacy counter once the UI updates (~june 2015)
this._trackLegacyPerformance(PerfCounterType.RequestsPerSec, requestsPerSec);
this._trackLegacyPerformance(PerfCounterType.RequestExecutionTime, AutoCollectPerformance._lastRequestExecutionTime);
}
this._lastRequests = requests;
};
AutoCollectPerformance.prototype.dispose = function () {
AutoCollectPerformance.INSTANCE = null;
this._isInitialized = false;
};
AutoCollectPerformance._totalRequestCount = 0;
AutoCollectPerformance._totalFailedRequestCount = 0;
AutoCollectPerformance._lastRequestExecutionTime = 0;
return AutoCollectPerformance;
}());
module.exports = AutoCollectPerformance;

View File

@@ -0,0 +1,45 @@
"use strict";
/**
* Base class for helpers that read data from HTTP requst/response objects and convert them
* into the telemetry contract objects.
*/
var RequestParser = (function () {
function RequestParser() {
}
/**
* Gets a url parsed out from request options
*/
RequestParser.prototype.getUrl = function () {
return this.url;
};
RequestParser.prototype.RequestParser = function () {
this.startTime = +new Date();
};
RequestParser.prototype._setStatus = function (status, error, properties) {
var endTime = +new Date();
this.duration = endTime - this.startTime;
this.statusCode = status;
if (error) {
if (!properties) {
properties = {};
}
if (typeof error === "string") {
properties["error"] = error;
}
else if (error instanceof Error) {
properties["error"] = error.message;
}
else if (typeof error === "object") {
for (var key in error) {
properties[key] = error[key] && error[key].toString && error[key].toString();
}
}
}
this.properties = properties;
};
RequestParser.prototype._isSuccess = function () {
return (0 < this.statusCode) && (this.statusCode < 400);
};
return RequestParser;
}());
module.exports = RequestParser;

View File

@@ -0,0 +1,145 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var url = require("url");
var ContractsModule = require("../Library/Contracts");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var RequestParser = require("./RequestParser");
/**
* Helper class to read data from the requst/response objects and convert them into the telemetry contract
*/
var ServerRequestParser = (function (_super) {
__extends(ServerRequestParser, _super);
function ServerRequestParser(request, requestId) {
_super.call(this);
if (request) {
this.requestId = requestId || Util.newGuid();
this.method = request.method;
this.url = this._getAbsoluteUrl(request);
this.startTime = +new Date();
this.rawHeaders = request.headers || request.rawHeaders;
this.socketRemoteAddress = request.socket && request.socket.remoteAddress;
this.userAgent = request.headers && request.headers["user-agent"];
this.sourceIKeyHash =
request.headers && request.headers[RequestResponseHeaders.sourceInstrumentationKeyHeader];
this.parentId =
request.headers && request.headers[RequestResponseHeaders.parentIdHeader];
this.operationId =
request.headers && request.headers[RequestResponseHeaders.rootIdHeader];
if (request.connection) {
this.connectionRemoteAddress = request.connection.remoteAddress;
this.legacySocketRemoteAddress = request.connection["socket"] && request.connection["socket"].remoteAddress;
}
}
}
ServerRequestParser.prototype.onError = function (error, properties, ellapsedMilliseconds) {
this._setStatus(undefined, error, properties);
};
ServerRequestParser.prototype.onResponse = function (response, properties, ellapsedMilliseconds) {
this._setStatus(response.statusCode, undefined, properties);
if (ellapsedMilliseconds) {
this.duration = ellapsedMilliseconds;
}
};
ServerRequestParser.prototype.getRequestData = function () {
var requestData = new ContractsModule.Contracts.RequestData();
requestData.id = this.requestId;
requestData.name = this.method + " " + url.parse(this.url).pathname;
requestData.url = this.url;
requestData.source = this.sourceIKeyHash;
requestData.duration = Util.msToTimeSpan(this.duration);
requestData.responseCode = this.statusCode ? this.statusCode.toString() : null;
requestData.success = this._isSuccess();
requestData.properties = this.properties;
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.RequestData";
data.baseData = requestData;
return data;
};
ServerRequestParser.prototype.getRequestTags = function (tags) {
// create a copy of the context for requests since client info will be used here
var newTags = {};
for (var key in tags) {
newTags[key] = tags[key];
}
// don't override tags if they are already set
newTags[ServerRequestParser.keys.locationIp] = tags[ServerRequestParser.keys.locationIp] || this._getIp();
newTags[ServerRequestParser.keys.sessionId] = tags[ServerRequestParser.keys.sessionId] || this._getId("ai_session");
newTags[ServerRequestParser.keys.userId] = tags[ServerRequestParser.keys.userId] || this._getId("ai_user");
newTags[ServerRequestParser.keys.userAgent] = tags[ServerRequestParser.keys.userAgent] || this.userAgent;
newTags[ServerRequestParser.keys.operationName] = this.getOperationName(tags);
newTags[ServerRequestParser.keys.operationParentId] = this.getOperationParentId(tags);
newTags[ServerRequestParser.keys.operationId] = this.getOperationId(tags);
return newTags;
};
ServerRequestParser.prototype.getOperationId = function (tags) {
return tags[ServerRequestParser.keys.operationId] || this.operationId;
};
ServerRequestParser.prototype.getOperationParentId = function (tags) {
return tags[ServerRequestParser.keys.operationParentId] || this.parentId || this.getOperationId(tags);
};
ServerRequestParser.prototype.getOperationName = function (tags) {
return tags[ServerRequestParser.keys.operationName] || this.method + " " + url.parse(this.url).pathname;
};
ServerRequestParser.prototype.getRequestId = function () {
return this.requestId;
};
ServerRequestParser.prototype._getAbsoluteUrl = function (request) {
if (!request.headers) {
return request.url;
}
var encrypted = request.connection ? request.connection.encrypted : null;
var requestUrl = url.parse(request.url);
var pathName = requestUrl.pathname;
var search = requestUrl.search;
var absoluteUrl = url.format({
protocol: encrypted ? "https" : "http",
host: request.headers.host,
pathname: pathName,
search: search
});
return absoluteUrl;
};
ServerRequestParser.prototype._getIp = function () {
// regex to match ipv4 without port
// Note: including the port would cause the payload to be rejected by the data collector
var ipMatch = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/;
var check = function (str) {
var results = ipMatch.exec(str);
if (results) {
return results[0];
}
};
var ip = check(this.rawHeaders["x-forwarded-for"])
|| check(this.rawHeaders["x-client-ip"])
|| check(this.rawHeaders["x-real-ip"])
|| check(this.connectionRemoteAddress)
|| check(this.socketRemoteAddress)
|| check(this.legacySocketRemoteAddress);
// node v12 returns this if the address is "localhost"
if (!ip
&& this.connectionRemoteAddress
&& this.connectionRemoteAddress.substr
&& this.connectionRemoteAddress.substr(0, 2) === "::") {
ip = "127.0.0.1";
}
return ip;
};
ServerRequestParser.prototype._getId = function (name) {
var cookie = (this.rawHeaders && this.rawHeaders["cookie"] &&
typeof this.rawHeaders["cookie"] === 'string' && this.rawHeaders["cookie"]) || "";
var value = ServerRequestParser.parseId(Util.getCookie(name, cookie));
return value;
};
ServerRequestParser.parseId = function (cookieValue) {
return cookieValue.substr(0, cookieValue.indexOf('|'));
};
ServerRequestParser.keys = new ContractsModule.Contracts.ContextTagKeys();
return ServerRequestParser;
}(RequestParser));
module.exports = ServerRequestParser;

View File

@@ -0,0 +1,169 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var http = require("http");
var https = require("https");
var Logging = require("../Library/Logging");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var ServerRequestParser = require("./ServerRequestParser");
var CorrelationContextManager_1 = require("./CorrelationContextManager");
var AutoCollectServerRequests = (function () {
function AutoCollectServerRequests(client) {
if (!!AutoCollectServerRequests.INSTANCE) {
throw new Error("Server request tracking should be configured from the applicationInsights object");
}
AutoCollectServerRequests.INSTANCE = this;
this._client = client;
}
AutoCollectServerRequests.prototype.enable = function (isEnabled) {
this._isEnabled = isEnabled;
// Autocorrelation requires automatic monitoring of incoming server requests
// Disabling autocollection but enabling autocorrelation will still enable
// request monitoring but will not produce request events
if ((this._isAutoCorrelating || this._isEnabled) && !this._isInitialized) {
this.useAutoCorrelation(this._isAutoCorrelating);
this._initialize();
}
};
AutoCollectServerRequests.prototype.useAutoCorrelation = function (isEnabled) {
if (isEnabled && !this._isAutoCorrelating) {
CorrelationContextManager_1.CorrelationContextManager.enable();
}
else if (!isEnabled && this._isAutoCorrelating) {
CorrelationContextManager_1.CorrelationContextManager.disable();
}
this._isAutoCorrelating = isEnabled;
};
AutoCollectServerRequests.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectServerRequests.prototype.isAutoCorrelating = function () {
return this._isAutoCorrelating;
};
AutoCollectServerRequests.prototype._generateCorrelationContext = function (requestParser) {
if (!this._isAutoCorrelating) {
return;
}
return CorrelationContextManager_1.CorrelationContextManager.generateContextObject(requestParser.getRequestId(), requestParser.getOperationName(this._client.context.tags), requestParser.getOperationId(this._client.context.tags));
};
AutoCollectServerRequests.prototype._initialize = function () {
var _this = this;
this._isInitialized = true;
var originalHttpServer = http.createServer;
http.createServer = function (onRequest) {
// todo: get a pointer to the server so the IP address can be read from server.address
return originalHttpServer(function (request, response) {
// Set up correlation context
var requestParser = new ServerRequestParser(request);
var correlationContext = _this._generateCorrelationContext(requestParser);
CorrelationContextManager_1.CorrelationContextManager.runWithContext(correlationContext, function () {
if (_this._isEnabled) {
// Auto collect request
AutoCollectServerRequests.trackRequest(_this._client, request, response, null, requestParser);
}
if (typeof onRequest === "function") {
onRequest(request, response);
}
});
});
};
var originalHttpsServer = https.createServer;
https.createServer = function (options, onRequest) {
return originalHttpsServer(options, function (request, response) {
// Set up correlation context
var requestParser = new ServerRequestParser(request);
var correlationContext = _this._generateCorrelationContext(requestParser);
CorrelationContextManager_1.CorrelationContextManager.runWithContext(correlationContext, function () {
if (_this._isEnabled) {
AutoCollectServerRequests.trackRequest(_this._client, request, response, null, requestParser);
}
if (typeof onRequest === "function") {
onRequest(request, response);
}
});
});
};
};
/**
* Tracks a request synchronously (doesn't wait for response 'finish' event)
*/
AutoCollectServerRequests.trackRequestSync = function (client, request, response, ellapsedMilliseconds, properties, error) {
if (!request || !response || !client) {
Logging.info("AutoCollectServerRequests.trackRequestSync was called with invalid parameters: ", !request, !response, !client);
return;
}
AutoCollectServerRequests.addResponseIKeyHeader(client, response);
// store data about the request
var correlationContext = CorrelationContextManager_1.CorrelationContextManager.getCurrentContext();
var requestParser = new ServerRequestParser(request, (correlationContext && correlationContext.operation.parentId) || Util.newGuid());
// Overwrite correlation context with request parser results
if (correlationContext) {
correlationContext.operation.id = requestParser.getOperationId(client.context.tags) || correlationContext.operation.id;
correlationContext.operation.name = requestParser.getOperationName(client.context.tags) || correlationContext.operation.name;
correlationContext.operation.parentId = requestParser.getRequestId() || correlationContext.operation.parentId;
}
AutoCollectServerRequests.endRequest(client, requestParser, request, response, ellapsedMilliseconds, properties, error);
};
/**
* Tracks a request by listening to the response 'finish' event
*/
AutoCollectServerRequests.trackRequest = function (client, request, response, properties, _requestParser) {
if (!request || !response || !client) {
Logging.info("AutoCollectServerRequests.trackRequest was called with invalid parameters: ", !request, !response, !client);
return;
}
// store data about the request
var correlationContext = CorrelationContextManager_1.CorrelationContextManager.getCurrentContext();
var requestParser = _requestParser || new ServerRequestParser(request, correlationContext && correlationContext.operation.parentId || Util.newGuid());
if (Util.canIncludeCorrelationHeader(client, requestParser.getUrl())) {
AutoCollectServerRequests.addResponseIKeyHeader(client, response);
}
// Overwrite correlation context with request parser results (if not an automatic track. we've already precalculated the correlation context in that case)
if (correlationContext && !_requestParser) {
correlationContext.operation.id = requestParser.getOperationId(client.context.tags) || correlationContext.operation.id;
correlationContext.operation.name = requestParser.getOperationName(client.context.tags) || correlationContext.operation.name;
correlationContext.operation.parentId = requestParser.getOperationParentId(client.context.tags) || correlationContext.operation.parentId;
}
// response listeners
if (response.once) {
response.once("finish", function () {
AutoCollectServerRequests.endRequest(client, requestParser, request, response, null, properties, null);
});
}
// track a failed request if an error is emitted
if (request.on) {
request.on("error", function (error) {
AutoCollectServerRequests.endRequest(client, requestParser, request, response, null, properties, error);
});
}
};
/**
* Add the target ikey hash to the response headers, if not already provided.
*/
AutoCollectServerRequests.addResponseIKeyHeader = function (client, response) {
if (client.config && client.config.instrumentationKeyHash &&
response.getHeader && response.setHeader &&
!response.getHeader(RequestResponseHeaders.targetInstrumentationKeyHeader) &&
!response.headersSent) {
response.setHeader(RequestResponseHeaders.targetInstrumentationKeyHeader, client.config.instrumentationKeyHash);
}
};
AutoCollectServerRequests.endRequest = function (client, requestParser, request, response, ellapsedMilliseconds, properties, error) {
if (error) {
requestParser.onError(error, properties, ellapsedMilliseconds);
}
else {
requestParser.onResponse(response, properties, ellapsedMilliseconds);
}
var context = { "http.ServerRequest": request, "http.ServerResponse": response };
var data = requestParser.getRequestData();
var tags = requestParser.getRequestTags(client.context.tags);
client.track(data, tags, context);
};
AutoCollectServerRequests.prototype.dispose = function () {
AutoCollectServerRequests.INSTANCE = null;
this._isInitialized = false;
};
return AutoCollectServerRequests;
}());
module.exports = AutoCollectServerRequests;

View File

@@ -0,0 +1,107 @@
"use strict";
var Logging = require("./Logging");
var Channel = (function () {
function Channel(isDisabled, getBatchSize, getBatchIntervalMs, sender) {
this._buffer = [];
this._lastSend = 0;
this._isDisabled = isDisabled;
this._getBatchSize = getBatchSize;
this._getBatchIntervalMs = getBatchIntervalMs;
this._sender = sender;
}
/**
* Enable or disable offline mode
*/
Channel.prototype.setOfflineMode = function (value, resendInterval) {
this._sender.setOfflineMode(value, resendInterval);
};
/**
* Add a telemetry item to the send buffer
*/
Channel.prototype.send = function (envelope) {
var _this = this;
// if master off switch is set, don't send any data
if (this._isDisabled()) {
// Do not send/save data
return;
}
// validate input
if (!envelope) {
Logging.warn("Cannot send null/undefined telemetry");
return;
}
// check if the incoming payload is too large, truncate if necessary
var payload = this._stringify(envelope);
if (typeof payload !== "string") {
return;
}
// enqueue the payload
this._buffer.push(payload);
// flush if we would exceed the max-size limit by adding this item
if (this._buffer.length >= this._getBatchSize()) {
this.triggerSend(false);
return;
}
// ensure an invocation timeout is set if anything is in the buffer
if (!this._timeoutHandle && this._buffer.length > 0) {
this._timeoutHandle = setTimeout(function () {
_this._timeoutHandle = null;
_this.triggerSend(false);
}, this._getBatchIntervalMs());
}
};
Channel.prototype.handleCrash = function (envelope) {
if (envelope) {
var payload = this._stringify(envelope);
if (typeof payload === "string") {
this._buffer.push(payload);
this.triggerSend(true);
}
else {
Logging.warn("Could not send crash", envelope);
}
}
else {
Logging.warn("handleCrash was called with empty payload", envelope);
}
};
/**
* Immediately send buffered data
*/
Channel.prototype.triggerSend = function (isNodeCrashing, callback) {
var bufferIsEmpty = this._buffer.length < 1;
if (!bufferIsEmpty) {
// compose an array of payloads
var batch = this._buffer.join("\n");
// invoke send
if (isNodeCrashing) {
this._sender.saveOnCrash(batch);
if (typeof callback === "function") {
callback("data saved on crash");
}
}
else {
this._sender.send(new Buffer(batch), callback);
}
}
// update lastSend time to enable throttling
this._lastSend = +new Date;
// clear buffer
this._buffer.length = 0;
clearTimeout(this._timeoutHandle);
this._timeoutHandle = null;
if (bufferIsEmpty && typeof callback === "function") {
callback("no data to send");
}
};
Channel.prototype._stringify = function (envelope) {
try {
return JSON.stringify(envelope);
}
catch (error) {
Logging.warn("Failed to serialize payload", error, envelope);
}
};
return Channel;
}());
module.exports = Channel;

View File

@@ -0,0 +1,253 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var url = require("url");
var Config = require("./Config");
var Context = require("./Context");
var ExceptionTracking = require("../AutoCollection/Exceptions");
var ContractsModule = require("../Library/Contracts");
var Channel = require("./Channel");
var ServerRequestTracking = require("../AutoCollection/ServerRequests");
var ClientRequestTracking = require("../AutoCollection/ClientRequests");
var CorrelationContextManager_1 = require("../AutoCollection/CorrelationContextManager");
var Sender = require("./Sender");
var Util = require("./Util");
var Logging = require("./Logging");
var Client = (function () {
/**
* Constructs a new client of the client
* @param iKey the instrumentation key to use (read from environment variable if not specified)
*/
function Client(iKey) {
this._telemetryProcessors = [];
var config = new Config(iKey);
this.config = config;
this.context = new Context();
this.commonProperties = {};
var sender = new Sender(function () { return config.endpointUrl; });
this.channel = new Channel(function () { return config.disableAppInsights; }, function () { return config.maxBatchSize; }, function () { return config.maxBatchIntervalMs; }, sender);
}
/**
* Log a user action or other occurrence.
* @param name A string to identify this event in the portal.
* @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
Client.prototype.trackEvent = function (name, properties, measurements) {
var event = new ContractsModule.Contracts.EventData();
event.name = name;
event.properties = properties;
event.measurements = measurements;
var data = new ContractsModule.Contracts.Data();
data.baseType = "EventData";
data.baseData = event;
this.track(data);
};
/**
* Log a trace message
* @param message A string to identify this event in the portal.
* @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty.
*/
Client.prototype.trackTrace = function (message, severityLevel, properties) {
var trace = new ContractsModule.Contracts.MessageData();
trace.message = message;
trace.properties = properties;
if (!isNaN(severityLevel)) {
trace.severityLevel = severityLevel;
}
else {
trace.severityLevel = ContractsModule.Contracts.SeverityLevel.Information;
}
var data = new ContractsModule.Contracts.Data();
data.baseType = "MessageData";
data.baseData = trace;
this.track(data);
};
/**
* Log an exception you have caught.
* @param exception An Error from a catch clause, or the string error message.
* @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty.
* @param measurements map[string, number] - metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
Client.prototype.trackException = function (exception, properties, measurements) {
if (!Util.isError(exception)) {
exception = new Error(exception);
}
var data = ExceptionTracking.getExceptionData(exception, true, properties, measurements);
this.track(data);
};
/**
* * Log a numeric value that is not associated with a specific event. Typically used to send regular reports of performance indicators.
* To send a single measurement, use just the first two parameters. If you take measurements very frequently, you can reduce the
* telemetry bandwidth by aggregating multiple measurements and sending the resulting average at intervals.
*
* @param name A string that identifies the metric.
* @param value The value of the metric
* @param count the number of samples used to get this value
* @param min the min sample for this set
* @param max the max sample for this set
* @param stdDev the standard deviation of the set
*/
Client.prototype.trackMetric = function (name, value, count, min, max, stdDev, properties) {
var metrics = new ContractsModule.Contracts.MetricData(); // todo: enable client-batching of these
metrics.metrics = [];
var metric = new ContractsModule.Contracts.DataPoint();
metric.count = !isNaN(count) ? count : 1;
metric.kind = ContractsModule.Contracts.DataPointType.Aggregation;
metric.max = !isNaN(max) ? max : value;
metric.min = !isNaN(min) ? min : value;
metric.name = name;
metric.stdDev = !isNaN(stdDev) ? stdDev : 0;
metric.value = value;
metrics.metrics.push(metric);
metrics.properties = properties;
var data = new ContractsModule.Contracts.Data();
data.baseType = "MetricData";
data.baseData = metrics;
this.track(data);
};
Client.prototype.trackRequestSync = function (request, response, ellapsedMilliseconds, properties, error) {
ServerRequestTracking.trackRequestSync(this, request, response, ellapsedMilliseconds, properties, error);
};
Client.prototype.trackRequest = function (request, response, properties) {
ServerRequestTracking.trackRequest(this, request, response, properties);
};
Client.prototype.trackDependencyRequest = function (requestOptions, request, properties) {
ClientRequestTracking.trackRequest(this, requestOptions, request, properties);
};
Client.prototype.trackDependency = function (name, commandName, elapsedTimeMs, success, dependencyTypeName, properties, async, target) {
if (properties === void 0) { properties = {}; }
if (async === void 0) { async = false; }
if (target === void 0) { target = null; }
if (!target && commandName) {
target = url.parse(commandName).host;
}
var remoteDependency = new ContractsModule.Contracts.RemoteDependencyData();
remoteDependency.name = name;
remoteDependency.data = commandName;
remoteDependency.target = target;
remoteDependency.duration = Util.msToTimeSpan(elapsedTimeMs);
remoteDependency.success = success;
remoteDependency.type = dependencyTypeName;
remoteDependency.properties = properties;
var data = new ContractsModule.Contracts.Data();
data.baseType = "RemoteDependencyData";
data.baseData = remoteDependency;
this.track(data);
};
/**
* Immediately send all queued telemetry.
*/
Client.prototype.sendPendingData = function (callback) {
this.channel.triggerSend(false, callback);
};
Client.prototype.getEnvelope = function (data, tagOverrides) {
if (data && data.baseData) {
data.baseData.ver = 2;
// if no properties are specified just add the common ones
if (!data.baseData.properties) {
data.baseData.properties = this.commonProperties;
}
else {
// otherwise, check each of the common ones
for (var name in this.commonProperties) {
// only override if the property `name` has not been set on this item
if (!data.baseData.properties[name]) {
data.baseData.properties[name] = this.commonProperties[name];
}
}
}
}
// sanitize properties
data.baseData.properties = Util.validateStringMap(data.baseData.properties);
var iKey = this.config.instrumentationKey;
var envelope = new ContractsModule.Contracts.Envelope();
envelope.data = data;
envelope.iKey = iKey;
// this is kind of a hack, but the envelope name is always the same as the data name sans the chars "data"
envelope.name =
"Microsoft.ApplicationInsights." +
iKey.replace(/-/g, "") +
"." +
data.baseType.substr(0, data.baseType.length - 4);
envelope.tags = this.getTags(tagOverrides);
envelope.time = (new Date()).toISOString();
envelope.ver = 1;
return envelope;
};
/**
* Generic track method for all telemetry types
* @param data the telemetry to send
* @param tagOverrides the context tags to use for this telemetry which overwrite default context values
*/
Client.prototype.track = function (data, tagOverrides, contextObjects) {
var envelope = this.getEnvelope(data, tagOverrides);
var accepted = this.runTelemetryProcessors(envelope, contextObjects);
if (accepted) {
this.channel.send(envelope);
}
};
/**
* Adds telemetry processor to the collection. Telemetry processors will be called one by one
* before telemetry item is pushed for sending and in the order they were added.
*
* @param telemetryProcessor function, takes Envelope, and optional context object and returns boolean
*/
Client.prototype.addTelemetryProcessor = function (telemetryProcessor) {
this._telemetryProcessors.push(telemetryProcessor);
};
/*
* Removes all telemetry processors
*/
Client.prototype.clearTelemetryProcessors = function () {
this._telemetryProcessors = [];
};
Client.prototype.runTelemetryProcessors = function (envelope, contextObjects) {
var accepted = true;
var telemetryProcessorsCount = this._telemetryProcessors.length;
if (telemetryProcessorsCount === 0) {
return accepted;
}
contextObjects = contextObjects || {};
contextObjects['correlationContext'] = CorrelationContextManager_1.CorrelationContextManager.getCurrentContext();
for (var i = 0; i < telemetryProcessorsCount; ++i) {
try {
var processor = this._telemetryProcessors[i];
if (processor) {
if (processor.apply(null, [envelope, contextObjects]) === false) {
accepted = false;
break;
}
}
}
catch (error) {
accepted = false;
Logging.warn("One of telemetry processors failed, telemetry item will not be sent.", error, envelope);
}
}
return accepted;
};
Client.prototype.getTags = function (tagOverrides) {
var correlationContext = CorrelationContextManager_1.CorrelationContextManager.getCurrentContext();
// Make a copy of context tags so we don't alter the actual object
// Also perform tag overriding
var newTags = {};
for (var key in this.context.tags) {
newTags[key] = this.context.tags[key];
}
for (var key in tagOverrides) {
newTags[key] = tagOverrides[key];
}
if (!correlationContext) {
return newTags;
}
// Fill in internally-populated values if not already set
if (correlationContext) {
newTags[this.context.keys.operationId] = newTags[this.context.keys.operationId] || correlationContext.operation.id;
newTags[this.context.keys.operationName] = newTags[this.context.keys.operationName] || correlationContext.operation.name;
newTags[this.context.keys.operationParentId] = newTags[this.context.keys.operationParentId] || correlationContext.operation.parentId;
}
return newTags;
};
return Client;
}());
module.exports = Client;

View File

@@ -0,0 +1,44 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var crypto = require('crypto');
var Config = (function () {
function Config(instrumentationKey) {
this.instrumentationKey = instrumentationKey || Config._getInstrumentationKey();
this.instrumentationKeyHash = Config._getStringHashBase64(this.instrumentationKey);
this.endpointUrl = "https://dc.services.visualstudio.com/v2/track";
this.sessionRenewalMs = 30 * 60 * 1000;
this.sessionExpirationMs = 24 * 60 * 60 * 1000;
this.maxBatchSize = 250;
this.maxBatchIntervalMs = 15000;
this.disableAppInsights = false;
this.correlationHeaderExcludedDomains = [
"*.blob.core.windows.net",
"*.blob.core.chinacloudapi.cn",
"*.blob.core.cloudapi.de",
"*.blob.core.usgovcloudapi.net"];
}
Config._getInstrumentationKey = function () {
// check for both the documented env variable and the azure-prefixed variable
var iKey = process.env[Config.ENV_iKey]
|| process.env[Config.ENV_azurePrefix + Config.ENV_iKey]
|| process.env[Config.legacy_ENV_iKey]
|| process.env[Config.ENV_azurePrefix + Config.legacy_ENV_iKey];
if (!iKey || iKey == "") {
throw new Error("Instrumentation key not found, pass the key in the config to this method or set the key in the environment variable APPINSIGHTS_INSTRUMENTATIONKEY before starting the server");
}
return iKey;
};
Config._getStringHashBase64 = function (value) {
var hash = crypto.createHash('sha256');
hash.update(value);
var result = hash.digest('base64');
return result;
};
// Azure adds this prefix to all environment variables
Config.ENV_azurePrefix = "APPSETTING_";
// This key is provided in the readme
Config.ENV_iKey = "APPINSIGHTS_INSTRUMENTATIONKEY";
Config.legacy_ENV_iKey = "APPINSIGHTS_INSTRUMENTATION_KEY";
return Config;
}());
module.exports = Config;

View File

@@ -0,0 +1,62 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var os = require("os");
var ContractsModule = require("../Library/Contracts");
var Logging = require("./Logging");
var Context = (function () {
function Context(packageJsonPath) {
this.keys = new ContractsModule.Contracts.ContextTagKeys();
this.tags = {};
this._loadApplicationContext();
this._loadDeviceContext();
this._loadInternalContext();
}
Context.prototype._loadApplicationContext = function (packageJsonPath) {
var version = "unknown";
var description = undefined;
try {
// note: this should return the host package.json
var packageJson = require(packageJsonPath || "../../../package.json");
if (packageJson) {
if (typeof packageJson.version === "string") {
version = packageJson.version;
}
if (typeof packageJson.description === "string") {
description = packageJson.description;
}
}
}
catch (exception) {
Logging.info("unable to read app version: ", exception);
}
this.tags[this.keys.applicationVersion] = version;
// TODO: consider sending it as a custom property
//if(description) {
// this.tags[this.keys.applicationBuild] = description;
//}
};
Context.prototype._loadDeviceContext = function () {
this.tags[this.keys.deviceId] = "";
this.tags[this.keys.cloudRoleInstance] = os && os.hostname();
this.tags[this.keys.deviceOSVersion] = os && os.type() + " " + os && os.release();
// not yet supported tags
this.tags["ai.device.osArchitecture"] = os && os.arch();
this.tags["ai.device.osPlatform"] = os && os.platform();
};
Context.prototype._loadInternalContext = function () {
var version = "unknown";
try {
// note: this should return the appInsights package.json
var packageJson = require("../package.json");
if (packageJson && typeof packageJson.version === "string") {
version = packageJson.version;
}
}
catch (exception) {
Logging.info("unable to read SDK version: " + exception);
}
this.tags[this.keys.internalSdkVersion] = "node:" + version || "unknown";
};
return Context;
}());
module.exports = Context;

View File

@@ -0,0 +1,225 @@
// this file is manually constructed and many types and fields here are deprecated.
// Need to switch to use Declarations\Constracts\Generated instead
// This will be consistent with JavaScript SDK
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Contracts;
(function (Contracts) {
(function (DataPointType) {
DataPointType[DataPointType["Measurement"] = 0] = "Measurement";
DataPointType[DataPointType["Aggregation"] = 1] = "Aggregation";
})(Contracts.DataPointType || (Contracts.DataPointType = {}));
var DataPointType = Contracts.DataPointType;
(function (SeverityLevel) {
SeverityLevel[SeverityLevel["Verbose"] = 0] = "Verbose";
SeverityLevel[SeverityLevel["Information"] = 1] = "Information";
SeverityLevel[SeverityLevel["Warning"] = 2] = "Warning";
SeverityLevel[SeverityLevel["Error"] = 3] = "Error";
SeverityLevel[SeverityLevel["Critical"] = 4] = "Critical";
})(Contracts.SeverityLevel || (Contracts.SeverityLevel = {}));
var SeverityLevel = Contracts.SeverityLevel;
var ContextTagKeys = (function () {
function ContextTagKeys() {
this.applicationVersion = "ai.application.ver";
this.deviceId = "ai.device.id";
this.deviceLocale = "ai.device.locale";
this.deviceModel = "ai.device.model";
this.deviceOEMName = "ai.device.oemName";
this.deviceOSVersion = "ai.device.osVersion";
this.deviceType = "ai.device.type";
this.locationIp = "ai.location.ip";
this.operationId = "ai.operation.id";
this.operationName = "ai.operation.name";
this.operationParentId = "ai.operation.parentId";
this.operationSyntheticSource = "ai.operation.syntheticSource";
this.operationCorrelationVector = "ai.operation.correlationVector";
this.sessionId = "ai.session.id";
this.sessionIsFirst = "ai.session.isFirst";
this.userAccountId = "ai.user.accountId";
this.userAgent = "ai.user.userAgent";
this.userId = "ai.user.id";
this.userAuthUserId = "ai.user.authUserId";
this.cloudRole = "ai.cloud.role";
this.cloudRoleInstance = "ai.cloud.roleInstance";
this.internalSdkVersion = "ai.internal.sdkVersion";
this.internalAgentVersion = "ai.internal.agentVersion";
this.internalNodeName = "ai.internal.nodeName";
}
return ContextTagKeys;
}());
Contracts.ContextTagKeys = ContextTagKeys;
var Domain = (function () {
function Domain() {
}
return Domain;
}());
Contracts.Domain = Domain;
var Data = (function () {
function Data() {
}
return Data;
}());
Contracts.Data = Data;
var Envelope = (function () {
function Envelope() {
this.ver = 1;
// the 'name' property must be initialized before 'tags' and/or 'data'.
this.name = "";
// the 'time' property must be initialized before 'tags' and/or 'data'.
this.time = "";
this.sampleRate = 100.0;
this.tags = {};
}
return Envelope;
}());
Contracts.Envelope = Envelope;
var EventData = (function (_super) {
__extends(EventData, _super);
function EventData() {
_super.call(this);
this.ver = 2;
this.properties = {};
this.measurements = {};
_super.call(this);
}
return EventData;
}(Contracts.Domain));
Contracts.EventData = EventData;
var MessageData = (function (_super) {
__extends(MessageData, _super);
function MessageData() {
_super.call(this);
this.ver = 2;
this.properties = {};
_super.call(this);
}
return MessageData;
}(Contracts.Domain));
Contracts.MessageData = MessageData;
var ExceptionData = (function (_super) {
__extends(ExceptionData, _super);
function ExceptionData() {
_super.call(this);
this.ver = 2;
this.exceptions = [];
this.properties = {};
this.measurements = {};
}
return ExceptionData;
}(Contracts.Domain));
Contracts.ExceptionData = ExceptionData;
var StackFrame = (function () {
function StackFrame() {
}
return StackFrame;
}());
Contracts.StackFrame = StackFrame;
var ExceptionDetails = (function () {
function ExceptionDetails() {
this.hasFullStack = true;
this.parsedStack = [];
}
return ExceptionDetails;
}());
Contracts.ExceptionDetails = ExceptionDetails;
var DataPoint = (function () {
function DataPoint() {
this.kind = Contracts.DataPointType.Measurement;
}
return DataPoint;
}());
Contracts.DataPoint = DataPoint;
var MetricData = (function (_super) {
__extends(MetricData, _super);
function MetricData() {
_super.call(this);
this.ver = 2;
this.metrics = [];
this.properties = {};
_super.call(this);
}
return MetricData;
}(Contracts.Domain));
Contracts.MetricData = MetricData;
var PageViewData = (function (_super) {
__extends(PageViewData, _super);
function PageViewData() {
_super.call(this);
this.ver = 2;
this.properties = {};
this.measurements = {};
_super.call(this);
}
return PageViewData;
}(Contracts.EventData));
Contracts.PageViewData = PageViewData;
var PageViewPerfData = (function (_super) {
__extends(PageViewPerfData, _super);
function PageViewPerfData() {
_super.call(this);
this.ver = 2;
this.properties = {};
this.measurements = {};
}
return PageViewPerfData;
}(Contracts.PageViewData));
Contracts.PageViewPerfData = PageViewPerfData;
var RemoteDependencyDataConstants = (function () {
function RemoteDependencyDataConstants() {
}
RemoteDependencyDataConstants.TYPE_HTTP = "Http";
return RemoteDependencyDataConstants;
}());
Contracts.RemoteDependencyDataConstants = RemoteDependencyDataConstants;
var RemoteDependencyData = (function (_super) {
__extends(RemoteDependencyData, _super);
function RemoteDependencyData() {
_super.call(this);
this.ver = 2;
this.success = true;
this.properties = {};
this.measurements = {};
}
return RemoteDependencyData;
}(Contracts.Domain));
Contracts.RemoteDependencyData = RemoteDependencyData;
var AjaxCallData = (function (_super) {
__extends(AjaxCallData, _super);
function AjaxCallData() {
_super.call(this);
this.ver = 2;
this.properties = {};
this.measurements = {};
_super.call(this);
}
return AjaxCallData;
}(Contracts.PageViewData));
Contracts.AjaxCallData = AjaxCallData;
var RequestData = (function (_super) {
__extends(RequestData, _super);
function RequestData() {
_super.call(this);
this.ver = 2;
this.properties = {};
this.measurements = {};
}
return RequestData;
}(Contracts.Domain));
Contracts.RequestData = RequestData;
var PerformanceCounterData = (function (_super) {
__extends(PerformanceCounterData, _super);
function PerformanceCounterData() {
_super.call(this);
this.ver = 2;
this.kind = DataPointType.Aggregation;
this.properties = {};
_super.call(this);
}
return PerformanceCounterData;
}(Contracts.Domain));
Contracts.PerformanceCounterData = PerformanceCounterData;
})(Contracts = exports.Contracts || (exports.Contracts = {}));

View File

@@ -0,0 +1,28 @@
"use strict";
var Logging = (function () {
function Logging() {
}
Logging.info = function (message) {
var optionalParams = [];
for (var _i = 1; _i < arguments.length; _i++) {
optionalParams[_i - 1] = arguments[_i];
}
if (Logging.enableDebug) {
console.info(Logging.TAG + message, optionalParams);
}
};
Logging.warn = function (message) {
var optionalParams = [];
for (var _i = 1; _i < arguments.length; _i++) {
optionalParams[_i - 1] = arguments[_i];
}
if (!Logging.disableWarnings) {
console.warn(Logging.TAG + message, optionalParams);
}
};
Logging.enableDebug = false;
Logging.disableWarnings = false;
Logging.TAG = "ApplicationInsights:";
return Logging;
}());
module.exports = Logging;

View File

@@ -0,0 +1,22 @@
"use strict";
module.exports = {
/**
* Source instrumentation header that is added by an application while making http
* requests and retrieved by the other application when processing incoming requests.
*/
sourceInstrumentationKeyHeader: "x-ms-request-source-ikey",
/**
* Target instrumentation header that is added to the response and retrieved by the
* calling application when processing incoming responses.
*/
targetInstrumentationKeyHeader: "x-ms-request-target-ikey",
/**
* Header containing the id of the immidiate caller
*/
parentIdHeader: "x-ms-request-id",
/**
* Header containing the correlation id that kept the same for every telemetry item
* accross transactions
*/
rootIdHeader: "x-ms-request-root-id"
};

View File

@@ -0,0 +1,218 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var fs = require("fs");
var http = require("http");
var https = require("https");
var os = require("os");
var path = require("path");
var url = require("url");
var zlib = require("zlib");
var Logging = require("./Logging");
var AutoCollectClientRequests = require("../AutoCollection/ClientRequests");
var Sender = (function () {
function Sender(getUrl, onSuccess, onError) {
this._getUrl = getUrl;
this._onSuccess = onSuccess;
this._onError = onError;
this._enableOfflineMode = false;
this._resendInterval = Sender.WAIT_BETWEEN_RESEND;
}
/**
* Enable or disable offline mode
*/
Sender.prototype.setOfflineMode = function (value, resendInterval) {
this._enableOfflineMode = value;
if (typeof resendInterval === 'number' && resendInterval >= 0) {
this._resendInterval = Math.floor(resendInterval);
}
};
Sender.prototype.send = function (payload, callback) {
var _this = this;
var endpointUrl = this._getUrl();
if (endpointUrl && endpointUrl.indexOf("//") === 0) {
// use https if the config did not specify a protocol
endpointUrl = "https:" + endpointUrl;
}
// todo: investigate specifying an agent here: https://nodejs.org/api/http.html#http_class_http_agent
var parsedUrl = url.parse(endpointUrl);
var options = {
host: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname,
method: "POST",
headers: {
"Content-Type": "application/x-json-stream"
}
};
zlib.gzip(payload, function (err, buffer) {
var dataToSend = buffer;
if (err) {
Logging.warn(err);
dataToSend = payload; // something went wrong so send without gzip
options.headers["Content-Length"] = payload.length;
}
else {
options.headers["Content-Encoding"] = "gzip";
options.headers["Content-Length"] = buffer.length;
}
Logging.info(Sender.TAG, options);
// Ensure this request is not captured by auto-collection.
options[AutoCollectClientRequests.disableCollectionRequestOption] = true;
var requestCallback = function (res) {
res.setEncoding("utf-8");
//returns empty if the data is accepted
var responseString = "";
res.on("data", function (data) {
responseString += data;
});
res.on("end", function () {
Logging.info(Sender.TAG, responseString);
if (typeof _this._onSuccess === "function") {
_this._onSuccess(responseString);
}
if (typeof callback === "function") {
callback(responseString);
}
if (_this._enableOfflineMode) {
// try to send any cached events if the user is back online
if (res.statusCode === 200) {
setTimeout(function () { return _this._sendFirstFileOnDisk(); }, _this._resendInterval);
}
else if (res.statusCode === 206 ||
res.statusCode === 429 ||
res.statusCode === 439) {
_this._storeToDisk(payload);
}
}
});
};
var req = (parsedUrl.protocol == "https:") ?
https.request(options, requestCallback) :
http.request(options, requestCallback);
req.on("error", function (error) {
// todo: handle error codes better (group to recoverable/non-recoverable and persist)
Logging.warn(Sender.TAG, error);
_this._onErrorHelper(error);
if (typeof callback === "function") {
var errorMessage = "error sending telemetry";
if (error && (typeof error.toString === "function")) {
errorMessage = error.toString();
}
callback(errorMessage);
}
if (_this._enableOfflineMode) {
_this._storeToDisk(payload);
}
});
req.write(dataToSend);
req.end();
});
};
Sender.prototype.saveOnCrash = function (payload) {
this._storeToDiskSync(payload);
};
Sender.prototype._confirmDirExists = function (direcotry, callback) {
fs.exists(direcotry, function (exists) {
if (!exists) {
fs.mkdir(direcotry, function (err) {
callback(err);
});
}
else {
callback(null);
}
});
};
/**
* Stores the payload as a json file on disk in the temp direcotry
*/
Sender.prototype._storeToDisk = function (payload) {
var _this = this;
//ensure directory is created
var direcotry = path.join(os.tmpdir(), Sender.TEMPDIR);
this._confirmDirExists(direcotry, function (error) {
if (error) {
_this._onErrorHelper(error);
return;
}
//create file - file name for now is the timestamp, a better approach would be a UUID but that
//would require an external dependency
var fileName = new Date().getTime() + ".ai.json";
var fileFullPath = path.join(direcotry, fileName);
Logging.info(Sender.TAG, "saving data to disk at: " + fileFullPath);
fs.writeFile(fileFullPath, payload, function (error) { return _this._onErrorHelper(error); });
});
};
/**
* Stores the payload as a json file on disk using sync file operations
* this is used when storing data before crashes
*/
Sender.prototype._storeToDiskSync = function (payload) {
var direcotry = path.join(os.tmpdir(), Sender.TEMPDIR);
try {
if (!fs.existsSync(direcotry)) {
fs.mkdirSync(direcotry);
}
//create file - file name for now is the timestamp, a better approach would be a UUID but that
//would require an external dependency
var fileName = new Date().getTime() + ".ai.json";
var fileFullPath = path.join(direcotry, fileName);
Logging.info(Sender.TAG, "saving data before crash to disk at: " + fileFullPath);
fs.writeFileSync(fileFullPath, payload);
}
catch (error) {
this._onErrorHelper(error);
}
};
/**
* Check for temp telemetry files
* reads the first file if exist, deletes it and tries to send its load
*/
Sender.prototype._sendFirstFileOnDisk = function () {
var _this = this;
var tempDir = path.join(os.tmpdir(), Sender.TEMPDIR);
fs.exists(tempDir, function (exists) {
if (exists) {
fs.readdir(tempDir, function (error, files) {
if (!error) {
files = files.filter(function (f) { return path.basename(f).indexOf(".ai.json") > -1; });
if (files.length > 0) {
var firstFile = files[0];
var filePath = path.join(tempDir, firstFile);
fs.readFile(filePath, function (error, payload) {
if (!error) {
// delete the file first to prevent double sending
fs.unlink(filePath, function (error) {
if (!error) {
_this.send(payload);
}
else {
_this._onErrorHelper(error);
}
});
}
else {
_this._onErrorHelper(error);
}
});
}
}
else {
_this._onErrorHelper(error);
}
});
}
});
};
Sender.prototype._onErrorHelper = function (error) {
if (typeof this._onError === "function") {
this._onError(error);
}
};
Sender.TAG = "Sender";
// the amount of time the SDK will wait between resending cached data, this buffer is to avoid any throtelling from the service side
Sender.WAIT_BETWEEN_RESEND = 60 * 1000;
Sender.TEMPDIR = "appInsights-node";
return Sender;
}());
module.exports = Sender;

View File

@@ -0,0 +1,166 @@
"use strict";
var url = require("url");
var Logging = require("./Logging");
var Util = (function () {
function Util() {
}
/**
* helper method to access userId and sessionId cookie
*/
Util.getCookie = function (name, cookie) {
var value = "";
if (name && name.length && typeof cookie === "string") {
var cookieName = name + "=";
var cookies = cookie.split(";");
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
cookie = Util.trim(cookie);
if (cookie && cookie.indexOf(cookieName) === 0) {
value = cookie.substring(cookieName.length, cookies[i].length);
break;
}
}
}
return value;
};
/**
* helper method to trim strings (IE8 does not implement String.prototype.trim)
*/
Util.trim = function (str) {
if (typeof str === "string") {
return str.replace(/^\s+|\s+$/g, "");
}
else {
return "";
}
};
/**
* Convert an array of int32 to Base64 (no '==' at the end).
* MSB first.
*/
Util.int32ArrayToBase64 = function (array) {
var toChar = function (v, i) {
return String.fromCharCode((v >> i) & 0xFF);
};
var int32AsString = function (v) {
return toChar(v, 24) + toChar(v, 16) + toChar(v, 8) + toChar(v, 0);
};
var x = array.map(int32AsString).join("");
var s = new Buffer(x, "binary").toString("base64");
return s.substr(0, s.indexOf("="));
};
/**
* generate a random 32bit number (-0x80000000..0x7FFFFFFF).
*/
Util.random32 = function () {
return (0x100000000 * Math.random()) | 0;
};
/**
* generate GUID
*/
Util.newGuid = function () {
var hexValues = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
var oct = "", tmp;
for (var a = 0; a < 4; a++) {
tmp = Util.random32();
oct +=
hexValues[tmp & 0xF] +
hexValues[tmp >> 4 & 0xF] +
hexValues[tmp >> 8 & 0xF] +
hexValues[tmp >> 12 & 0xF] +
hexValues[tmp >> 16 & 0xF] +
hexValues[tmp >> 20 & 0xF] +
hexValues[tmp >> 24 & 0xF] +
hexValues[tmp >> 28 & 0xF];
}
// "Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively"
var clockSequenceHi = hexValues[8 + (Math.random() * 4) | 0];
return oct.substr(0, 8) + "-" + oct.substr(9, 4) + "-4" + oct.substr(13, 3) + "-" + clockSequenceHi + oct.substr(16, 3) + "-" + oct.substr(19, 12);
};
/**
* Check if an object is of type Array
*/
Util.isArray = function (obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
/**
* Check if an object is of type Error
*/
Util.isError = function (obj) {
return obj instanceof Error;
};
/**
* Check if an object is of type Date
*/
Util.isDate = function (obj) {
return Object.prototype.toString.call(obj) === "[object Date]";
};
/**
* Convert ms to c# time span format
*/
Util.msToTimeSpan = function (totalms) {
if (isNaN(totalms) || totalms < 0) {
totalms = 0;
}
var ms = "" + totalms % 1000;
var sec = "" + Math.floor(totalms / 1000) % 60;
var min = "" + Math.floor(totalms / (1000 * 60)) % 60;
var hour = "" + Math.floor(totalms / (1000 * 60 * 60)) % 24;
var days = Math.floor(totalms / (1000 * 60 * 60 * 24));
ms = ms.length === 1 ? "00" + ms : ms.length === 2 ? "0" + ms : ms;
sec = sec.length < 2 ? "0" + sec : sec;
min = min.length < 2 ? "0" + min : min;
hour = hour.length < 2 ? "0" + hour : hour;
var daysText = days > 0 ? days + "." : "";
return daysText + hour + ":" + min + ":" + sec + "." + ms;
};
/**
* Validate that an object is of type { [key: string]: string }
*/
Util.validateStringMap = function (obj) {
var map;
if (typeof obj === "object") {
map = {};
for (var field in obj) {
var property = obj[field];
var propertyType = typeof property;
if (propertyType !== "string") {
if (property != null && typeof property.toString === "function") {
property = property.toString();
}
else {
Logging.info("key: " + field + ", invalid property type: " + propertyType);
continue;
}
}
map[field] = property.trim(0, Util.MAX_PROPERTY_LENGTH);
}
}
else {
Logging.info("Invalid properties dropped from payload");
}
return map;
};
/**
* Checks if a request url is not on a excluded domain list
* and if it is safe to add correlation headers (x-ms-request-source-ikey, x-ms-request-target-ikey)
*/
Util.canIncludeCorrelationHeader = function (client, requestUrl) {
var excludedDomains = client && client.config && client.config.correlationHeaderExcludedDomains;
if (!excludedDomains || excludedDomains.length == 0 || !requestUrl) {
return true;
}
for (var i = 0; i < excludedDomains.length; i++) {
var regex = new RegExp(excludedDomains[i].replace(/\./g, "\.").replace(/\*/g, ".*"));
if (regex.test(url.parse(requestUrl).hostname)) {
return false;
}
}
return true;
};
Util.MAX_PROPERTY_LENGTH = 1024;
Util.document = typeof document !== "undefined" ? document : {};
return Util;
}());
module.exports = Util;

View File

@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright © Microsoft Corporation
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.

View File

@@ -0,0 +1,214 @@
# Application Insights for Node.js
[![NPM version](https://badge.fury.io/js/applicationinsights.svg)](http://badge.fury.io/js/applicationinsights)
[![Build Status](https://travis-ci.org/Microsoft/ApplicationInsights-node.js.svg?branch=master)](https://travis-ci.org/Microsoft/ApplicationInsights-node.js)
This project provides a [Visual Studio Application Insights](https://azure.microsoft.com/documentation/articles/app-insights-overview/) SDK for [Node.js](https://nodejs.org/). The SDK sends telemetry about the performance and usage of your live Node.js application to the Application Insights service. There you can analyze charts of request rates, response times, failures and dependencies, and diagnose issues using powerful search and aggregation tools.
The SDK provides automatic collection of incoming HTTP request rates and responses, performance counters (CPU, memory, RPS), and unhandled exceptions. In addition, you can add custom calls to track dependencies, metrics, or other events.
In versions of Node.js > 4.0 (and io.js > 3.3) the SDK provides automatic correlation of dependencies to requests (off by default, see Customized Usage below to enable).
## Requirements ##
**Install**
```
npm install applicationinsights
```
### Get an instrumentation key
[Create an Application Insights resource](https://azure.microsoft.com/documentation/articles/app-insights-create-new-resource/) where your telemetry will be displayed. This provides you with an instrumentation key that identifies the resource. (You can try the SDK without sending telemetry: set the instrumentation key to a non-empty string.)
## Usage ##
This will enable request monitoring, unhandled exception tracking, and system performance monitoring (CPU/Memory/RPS).
```javascript
import appInsights = require("applicationinsights");
appInsights.setup("<instrumentation_key>").start();
```
>The instrumentation key can also be set in the environment variable APPINSIGHTS_INSTRUMENTATIONKEY. If this is done, no argument is required when calling `appInsights.setup()` or `appInsights.getClient()`.
## Customized Usage ##
### Enabling automatic correlation
```javascript
import appInsights = require("applicationinsights");
appInsights.setup("<instrumentation_key>")
.setAutoDependencyCorrelation(true)
// no telemetry will be sent until .start() is called
.start();
```
> Be sure to call `require("applicationinsights")` before your other imports. This allows the SDK to do patching necessary for tracking correlation state before other libraries use patched methods. If you encounter conflicts with other libraries doing similar patching, place this import below those libraries.
### Disabling automatic collection
```javascript
import appInsights = require("applicationinsights");
appInsights.setup("<instrumentation_key>")
.setAutoCollectRequests(false)
.setAutoCollectPerformance(false)
.setAutoCollectExceptions(false)
.setAutoCollectDependencies(false)
// no telemetry will be sent until .start() is called
.start();
```
### Custom monitoring
```javascript
import appInsights = require("applicationinsights");
var client = appInsights.getClient();
client.trackEvent("custom event", {customProperty: "custom property value"});
client.trackException(new Error("handled exceptions can be logged with this method"));
client.trackMetric("custom metric", 3);
client.trackTrace("trace message");
```
### Telemetry Processor
```javascript
public addTelemetryProcessor(telemetryProcessor: (envelope: ContractsModule.Contracts.Envelope, context: { http.RequestOptions, http.ClientRequest, http.ClientResponse, correlationContext }) => boolean)
```
Adds a telemetry processor to the collection. Telemetry processors will be called one by one, in the order they were added, before the telemetry item is pushed for sending.
If one of the telemetry processors returns false then the telemetry item will not be sent.
If one of the telemetry processors throws an error then the telemetry item will not be sent.
All telemetry processors receive the envelope to modify before sending. They also receive a context object with relevant request information (if available)
as well as the request storage object returned by `appInsights.getCorrelationContext()` (if automatic dependency correlation is enabled).
**Example**
Add the below code before you send any telemetry, it will remove stack trace information from any Exception reported by the SDK.
```javascript
appInsights.client.addTelemetryProcessor((envelope) => {
if (envelope.data.baseType === "Microsoft.ApplicationInsights.ExceptionData") {
var data = envelope.data.baseData;
if (data.exceptions && data.exceptions.length > 0) {
for(var i = 0; i < data.exceptions.length; i++) {
var exception = data.exceptions[i];
exception.parsedStack = null;
exception.hasFullStack = false;
}
}
}
return true;
});
```
[Learn more about the telemetry API](https://azure.microsoft.com/documentation/articles/app-insights-api-custom-events-metrics/).
### Using multiple instrumentation keys
```javascript
import appInsights = require("applicationinsights");
// configure auto-collection with one instrumentation key
appInsights.setup("<instrumentation_key>").start();
// get a client for another instrumentation key
var otherClient = appInsights.getClient("<other_instrumentation_key>");
otherClient.trackEvent("custom event");
```
## Examples
### Tracking dependency
```javascript
import appInsights = require("applicationinsights");
var client = appInsights.getClient();
var startTime = Date.now();
// execute dependency call
var endTime = Date.now();
var elapsedTime = endTime - startTime;
var success = true;
client.trackDependency("dependency name", "command name", elapsedTime, success);
```
### Manual request tracking of all "GET" requests
```javascript
var http = require("http");
var appInsights = require("applicationinsights");
appInsights.setup("<instrumentation_key>")
.setAutoCollectRequests(false) // disable auto-collection of requests for this example
.start();
// assign common properties to all telemetry sent from the default client
appInsights.client.commonProperties = {
environment: process.env.SOME_ENV_VARIABLE
};
// track a system startup event
appInsights.client.trackEvent("server start");
// create server
var port = process.env.port || 1337
var server = http.createServer(function (req, res) {
// track all "GET" requests
if(req.method === "GET") {
appInsights.client.trackRequest(req, res);
}
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello World\n");
}).listen(port);
// track startup time of the server as a custom metric
var start = +new Date;
server.on("listening", () => {
var end = +new Date;
var duration = end - start;
appInsights.client.trackMetric("StartupTime", duration);
});
```
## Branches
- [master](https://github.com/Microsoft/ApplicationInsights-node.js/tree/master) contains the *latest* published release located on [NPM](https://www.npmjs.com/package/applicationinsights).
- [develop](https://github.com/Microsoft/ApplicationInsights-node.js/tree/develop) contains the code for the *next* release. Please send all pull requests to this branch.
## Links
* Follow the latest Application Insights changes and announcements on [ApplicationInsights Announcements](https://github.com/Microsoft/ApplicationInsights-Announcements)
* [Application Insights Home](https://github.com/Microsoft/ApplicationInsights-Home). The main repository for documentation of overall SDK offerings for all platforms.
* [SDK Release Schedule](https://github.com/Microsoft/ApplicationInsights-Home/wiki/SDK-Release-Schedule)
## Contributing
**Development environment**
* Install dev dependencies
```
npm install
```
* (optional) Set an environment variable to your instrumentation key
```
set APPINSIGHTS_INSTRUMENTATIONKEY=<insert_your_instrumentation_key_here>
```
* Run tests
```
npm test
```
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

View File

@@ -0,0 +1,47 @@
import "Domain.bond"
namespace AI
[Description("Instances of AvailabilityData represent the result of executing an availability test.")]
struct AvailabilityData
: Domain
{
[Description("Schema version")]
10: required int32 ver = 2;
[MaxStringLength("64")]
[Description("Identifier of a test run. Use it to correlate steps of test run and telemetry generated by the service.")]
[ActAsRequired("Renaming testRunId to id.")]
21: required string id;
[MaxStringLength("1024")]
[Description("Name of the test that these availability results represents.")]
[ActAsRequired("Renaming testName to name.")]
41: required string name;
[Description("Duration in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff")]
[CSType("TimeSpan")]
50: required string duration;
[ActAsRequired("Renaming result to success.")]
[Description("Success flag.")]
61: required bool success;
[MaxStringLength("1024")]
[Description("Name of the location where the test was run from.")]
70: string runLocation;
[MaxStringLength("8192")]
[Description("Diagnostic message for the result.")]
80: string message;
[Description("Collection of custom properties.")]
[MaxKeyLength("150")]
[MaxValueLength("8192")]
100: map<string, string> properties;
[Description("Collection of custom measurements.")]
[MaxKeyLength("150")]
200: map<string, double> measurements;
}

View File

@@ -0,0 +1,11 @@
namespace AI
[Description("Data struct to contain only C section with custom fields.")]
struct Base
{
[Name("ItemTypeName")]
[Description("Name of item (B section) if any. If telemetry data is derived straight from this, this should be null.")]
10: string baseType;
}

View File

@@ -0,0 +1,105 @@
namespace AI
[ContextContract("Emit")]
[PseudoType("JSMap")]
struct ContextTagKeys
{
[Description("Application version. Information in the application context fields is always about the application that is sending the telemetry.")]
[MaxStringLength("1024")]
10: string ApplicationVersion = "ai.application.ver";
[Description("Unique client device id. Computer name in most cases.")]
[MaxStringLength("1024")]
100: string DeviceId = "ai.device.id";
[Description("Device locale using <language>-<REGION> pattern, following RFC 5646. Example 'en-US'.")]
[MaxStringLength("64")]
115: string DeviceLocale = "ai.device.locale";
[Description("Model of the device the end user of the application is using. Used for client scenarios. If this field is empty then it is derived from the user agent.")]
[MaxStringLength("256")]
120: string DeviceModel = "ai.device.model";
[Description("Client device OEM name taken from the browser.")]
[MaxStringLength("256")]
130: string DeviceOEMName = "ai.device.oemName";
[Description("Operating system name and version of the device the end user of the application is using. If this field is empty then it is derived from the user agent. Example 'Windows 10 Pro 10.0.10586.0'")]
[MaxStringLength("256")]
140: string DeviceOSVersion = "ai.device.osVersion";
[Description("The type of the device the end user of the application is using. Used primarily to distinguish JavaScript telemetry from server side telemetry. Examples: 'PC', 'Phone', 'Browser'. 'PC' is the default value.")]
[MaxStringLength("64")]
160: string DeviceType = "ai.device.type";
[Description("The IP address of the client device. IPv4 and IPv6 is supported. Information in the location context fields is always about the end user. When telemetry is sent from a service, the location context is about the user that initiated the operation in the service.")]
[MaxStringLength("45")]
200: string LocationIp = "ai.location.ip";
[Description("A unique identifier for the operation instance. The operation.id is created by either a request or a page view. All other telemetry sets this to the value for the containing request or page view. Operation.id is used for finding all the telemetry items for a specific operation instance.")]
[MaxStringLength("128")]
300: string OperationId = "ai.operation.id";
[Description("The name (group) of the operation. The operation.name is created by either a request or a page view. All other telemetry items set this to the value for the containing request or page view. Operation.name is used for finding all the telemetry items for a group of operations (i.e. 'GET Home/Index').")]
[MaxStringLength("1024")]
305: string OperationName = "ai.operation.name";
[Description("The unique identifier of the telemetry item's immediate parent.")]
[MaxStringLength("128")]
310: string OperationParentId = "ai.operation.parentId";
[Description("Name of synthetic source. Some telemetry from the application may represent a synthetic traffic. It may be web crawler indexing the web site, site availability tests or traces from diagnostic libraries like Application Insights SDK itself.")]
[MaxStringLength("1024")]
320: string OperationSyntheticSource = "ai.operation.syntheticSource";
[Description("The correlation vector is a light weight vector clock which can be used to identify and order related events across clients and services.")]
[MaxStringLength("64")]
330: string OperationCorrelationVector = "ai.operation.correlationVector";
[Description("Session ID - the instance of the user's interaction with the app. Information in the session context fields is always about the end user. When telemetry is sent from a service, the session context is about the user that initiated the operation in the service.")]
[MaxStringLength("64")]
400: string SessionId = "ai.session.id";
[Description("Boolean value indicating whether the session identified by ai.session.id is first for the user or not.")]
[MaxStringLength("5")]
[Question("Should it be marked as JSType-bool for breeze?")]
405: string SessionIsFirst = "ai.session.isFirst";
[Description("In multi-tenant applications this is the account ID or name which the user is acting with. Examples may be subscription ID for Azure portal or blog name blogging platform.")]
[MaxStringLength("1024")]
505: string UserAccountId = "ai.user.accountId";
[Description("The browser's user agent string as reported by the browser. This property will be used to extract informaiton regarding the customer's browser but will not be stored. Use custom properties to store the original user agent.")]
[MaxStringLength("2048")]
510: string UserAgent = "ai.user.userAgent";
[Description("Anonymous user id. Represents the end user of the application. When telemetry is sent from a service, the user context is about the user that initiated the operation in the service.")]
[MaxStringLength("128")]
515: string UserId = "ai.user.id";
[Description("Authenticated user id. The opposite of ai.user.id, this represents the user with a friendly name. Since it's PII information it is not collected by default by most SDKs.")]
[MaxStringLength("1024")]
525: string UserAuthUserId = "ai.user.authUserId";
[Description("Name of the role the application is a part of. Maps directly to the role name in azure.")]
[MaxStringLength("256")]
705: string CloudRole = "ai.cloud.role";
[Description("Name of the instance where the application is running. Computer name for on-premisis, instance name for Azure.")]
[MaxStringLength("256")]
715: string CloudRoleInstance = "ai.cloud.roleInstance";
[Description("SDK version. See https://github.com/Microsoft/ApplicationInsights-Home/blob/master/SDK-AUTHORING.md#sdk-version-specification for information.")]
[MaxStringLength("64")]
1000: string InternalSdkVersion = "ai.internal.sdkVersion";
[Description("Agent version. Used to indicate the version of StatusMonitor installed on the computer if it is used for data collection.")]
[MaxStringLength("64")]
1001: string InternalAgentVersion = "ai.internal.agentVersion";
[Description("This is the node name used for billing purposes. Use it to override the standard detection of nodes.")]
[MaxStringLength("256")]
1002: string InternalNodeName = "ai.internal.nodeName";
}

View File

@@ -0,0 +1,13 @@
import "Base.bond"
namespace AI
[Description("Data struct to contain both B and C sections.")]
struct Data<TDomain>
: Base
{
[Name("Item")]
[Description("Container for data item (B section).")]
20: required TDomain baseData;
}

View File

@@ -0,0 +1,30 @@
import "DataPointType.bond"
namespace AI
[Description("Metric data single measurement.")]
struct DataPoint
{
[Description("Name of the metric.")]
[MaxStringLength("1024")]
10: required string name;
[Description("Metric type.")]
20: AI.DataPointType kind = Measurement;
[Description("Metric calculated value.")]
30: required double value;
[Description("Metric weight of the aggregated metric. Should not be set for a measurement.")]
40: nullable<int32> count;
[Description("Minimum value of the aggregated metric. Should not be set for a measurement.")]
50: nullable<double> min;
[Description("Maximum value of the aggregated metric. Should not be set for a measurement.")]
60: nullable<double> max;
[Description("Standard deviation of the aggregated metric. Should not be set for a measurement.")]
70: nullable<double> stdDev;
}

View File

@@ -0,0 +1,8 @@
namespace AI
[Description("Type of the metric data measurement.")]
enum DataPointType
{
Measurement,
Aggregation,
}

View File

@@ -0,0 +1,7 @@
namespace AI
[Description("The abstract common base of all domains.")]
struct Domain
{
}

View File

@@ -0,0 +1,49 @@
import "Base.bond"
namespace AI
[Description("System variables for a telemetry item.")]
struct Envelope
{
[Description("Envelope version. For internal use only. By assigning this the default, it will not be serialized within the payload unless changed to a value other than #1.")]
[Name("SchemaVersion")]
10: int32 ver = 1;
[Description("Type name of telemetry data item.")]
[Name("DataTypeName")]
[MaxStringLength("1024")]
20: required string name;
[Description("Event date time when telemetry item was created. This is the wall clock time on the client when the event was generated. There is no guarantee that the client's time is accurate. This field must be formatted in UTC ISO 8601 format, with a trailing 'Z' character, as described publicly on https://en.wikipedia.org/wiki/ISO_8601#UTC. Note: the number of decimal seconds digits provided are variable (and unspecified). Consumers should handle this, i.e. managed code consumers should not use format 'O' for parsing as it specifies a fixed length. Example: 2009-06-15T13:45:30.0000000Z.")]
[Name("DateTime")]
[CSType("DateTimeOffset")]
[JSType("Date")]
[HockeyAppMinDateOffsetFromNow("2592000000")]
[MinDateOffsetFromNow("172800000")]
[MaxDateOffsetFromNow("7200000")]
30: required string time;
[Name("SamplingRate")]
[Description("Sampling rate used in application. This telemetry item represents 1 / sampleRate actual telemetry items.")]
40: double sampleRate = 100.0;
[Description("Sequence field used to track absolute order of uploaded events.")]
[Name("SequenceNumber")]
[MaxStringLength("64")]
50: string seq;
[Description("The application's instrumentation key.")]
[Name("InstrumentationKey")]
[MaxStringLength("40")]
60: string iKey;
[Name("Tags")]
[TypeAlias("ContextTagKeys")]
[Description("Key/value collection of context properties. See ContextTagKeys for information on available properties.")]
500: map<string, string> tags;
[Name("TelemetryData")]
[Description("Telemetry data item.")]
999: Base data;
}

View File

@@ -0,0 +1,26 @@
import "Domain.bond"
namespace AI
[Description("Instances of Event represent structured event records that can be grouped and searched by their properties. Event data item also creates a metric of event count by name.")]
struct EventData
: Domain
{
[Description("Schema version")]
10: required int32 ver = 2;
[MaxStringLength("512")]
[Description("Event name. Keep it low cardinality to allow proper grouping and useful metrics.")]
[Question("Why Custom Event name is shorter than Request name or dependency name?")]
20: required string name;
[Description("Collection of custom properties.")]
[MaxKeyLength("150")]
[MaxValueLength("8192")]
100: map<string, string> properties;
[Description("Collection of custom measurements.")]
[MaxKeyLength("150")]
200: map<string, double> measurements;
}

View File

@@ -0,0 +1,33 @@
import "Domain.bond"
import "ExceptionDetails.bond"
import "SeverityLevel.bond"
namespace AI
[Description("An instance of Exception represents a handled or unhandled exception that occurred during execution of the monitored application.")]
struct ExceptionData
: Domain
{
[Description("Schema version")]
10: required int32 ver = 2;
[Description("Exception chain - list of inner exceptions.")]
50: required vector<ExceptionDetails> exceptions;
[Description("Severity level. Mostly used to indicate exception severity level when it is reported by logging library.")]
60: nullable<AI.SeverityLevel> severityLevel;
[Description("Identifier of where the exception was thrown in code. Used for exceptions grouping. Typically a combination of exception type and a function from the call stack.")]
[MaxStringLength("1024")]
80: string problemId;
[Description("Collection of custom properties.")]
[MaxKeyLength("150")]
[MaxValueLength("8192")]
100: map<string, string> properties;
[Description("Collection of custom measurements.")]
[MaxKeyLength("150")]
200: map<string, double> measurements;
}

View File

@@ -0,0 +1,32 @@
import "StackFrame.bond"
namespace AI
[Description("Exception details of the exception in a chain.")]
struct ExceptionDetails
{
[Description("In case exception is nested (outer exception contains inner one), the id and outerId properties are used to represent the nesting.")]
10: int32 id;
[Description("The value of outerId is a reference to an element in ExceptionDetails that represents the outer exception")]
20: int32 outerId;
[Description("Exception type name.")]
[MaxStringLength("1024")]
30: required string typeName;
[Description("Exception message.")]
[MaxStringLength("32768")]
40: required string message;
[Description("Indicates if full exception stack is provided in the exception. The stack may be trimmed, such as in the case of a StackOverflow exception.")]
50: bool hasFullStack = true;
[Description("Text describing the stack. Either stack or parsedStack should have a value.")]
[MaxStringLength("32768")]
60: string stack;
[Description("List of stack frames. Either stack or parsedStack should have a value.")]
70: vector<StackFrame> parsedStack;
}

View File

@@ -0,0 +1,25 @@
import "Domain.bond"
import "SeverityLevel.bond"
namespace AI
[Description("Instances of Message represent printf-like trace statements that are text-searched. Log4Net, NLog and other text-based log file entries are translated into intances of this type. The message does not have measurements.")]
struct MessageData
: Domain
{
[Description("Schema version")]
10: required int32 ver = 2;
[MaxStringLength("32768")]
[Description("Trace message")]
20: required string message;
[Description("Trace severity level.")]
30: nullable<AI.SeverityLevel> severityLevel;
[Description("Collection of custom properties.")]
[MaxKeyLength("150")]
[MaxValueLength("8192")]
100: map<string, string> properties;
}

View File

@@ -0,0 +1,21 @@
import "Domain.bond"
import "DataPoint.bond"
namespace AI
[Description("An instance of the Metric item is a list of measurements (single data points) and/or aggregations.")]
struct MetricData
: Domain
{
[Description("Schema version")]
10: required int32 ver = 2;
[Description("List of metrics.")]
20: required vector<DataPoint> metrics;
[Description("Collection of custom properties.")]
[MaxKeyLength("150")]
[MaxValueLength("8192")]
100: map<string, string> properties;
}

View File

@@ -0,0 +1,18 @@
import "EventData.bond"
namespace AI
[Description("An instance of PageView represents a generic action on a page like a button click. It is also the base type for PageView.")]
[Alias("PageviewData;PageEventData")]
struct PageViewData
: EventData
{
[MaxStringLength("2048")]
[Description("Request URL with all query string parameters")]
10: string url;
[CSType("TimeSpan")]
[Description("Request duration in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff. For a page view (PageViewData), this is the duration. For a page view with performance information (PageViewPerfData), this is the page load time.")]
20: string duration;
}

View File

@@ -0,0 +1,53 @@
import "Domain.bond"
namespace AI
[Description("An instance of Remote Dependency represents an interaction of the monitored component with a remote component/service like SQL or an HTTP endpoint.")]
struct RemoteDependencyData
: Domain
{
[Description("Schema version")]
10: required int32 ver = 2;
[MaxStringLength("1024")]
[Description("Name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template.")]
20: required string name;
[MaxStringLength("128")]
[Description("Identifier of a dependency call instance. Used for correlation with the request telemetry item corresponding to this dependency call.")]
30: string id;
[MaxStringLength("1024")]
[Description("Result code of a dependency call. Examples are SQL error code and HTTP status code.")]
40: string resultCode;
[CSType("TimeSpan")]
[Description("Request duration in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff.")]
[ActAsRequired("Renaming value to duration.")]
61: required string duration;
[Description("Indication of successfull or unsuccessfull call.")]
120: nullable<bool> success = true;
[MaxStringLength("8192")]
[Description("Command initiated by this dependency call. Examples are SQL statement and HTTP URL's with all query parameters.")]
151: string data;
[MaxStringLength("1024")]
[Description("Dependency type name. Very low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP.")]
162: string type;
[MaxStringLength("1024")]
[Description("Target site of a dependency call. Examples are server name, host address.")]
161: string target;
[Description("Collection of custom properties.")]
[MaxKeyLength("150")]
[MaxValueLength("8192")]
200: map<string, string> properties;
[Description("Collection of custom measurements.")]
[MaxKeyLength("150")]
300: map<string, double> measurements;
}

View File

@@ -0,0 +1,48 @@
import "Domain.bond"
namespace AI
[Description("An instance of Request represents completion of an external request to the application to do work and contains a summary of that request execution and the results.")]
struct RequestData
: Domain
{
[Description("Schema version")]
10: required int32 ver = 2;
[MaxStringLength("128")]
[Description("Identifier of a request call instance. Used for correlation between request and other telemetry items.")]
20: required string id;
[CSType("TimeSpan")]
[Description("Request duration in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff.")]
50: required string duration;
[MaxStringLength("1024")]
[Description("Result of a request execution. HTTP status code for HTTP requests.")]
60: required string responseCode;
[Description("Indication of successfull or unsuccessfull call.")]
70: required bool success;
[MaxStringLength("1024")]
[Description("Source of the request. Examples are the instrumentation key of the caller or the ip address of the caller.")]
29: string source;
[MaxStringLength("1024")]
[Description("Name of the request. Represents code path taken to process request. Low cardinality value to allow better grouping of requests. For HTTP requests it represents the HTTP method and URL path template like 'GET /values/{id}'.")]
30: string name;
[MaxStringLength("2048")]
[Description("Request URL with all query string parameters.")]
90: string url;
[Description("Collection of custom properties.")]
[MaxKeyLength("150")]
[MaxValueLength("8192")]
100: map<string, string> properties;
[Description("Collection of custom measurements.")]
[MaxKeyLength("150")]
200: map<string, double> measurements;
}

View File

@@ -0,0 +1,11 @@
namespace AI
[Description("Defines the level of severity for the event.")]
enum SeverityLevel
{
Verbose,
Information,
Warning,
Error,
Critical,
}

View File

@@ -0,0 +1,25 @@
namespace AI
[Description("Stack frame information.")]
struct StackFrame
{
[Description("Level in the call stack. For the long stacks SDK may not report every function in a call stack.")]
10: required int32 level;
[Description("Method name.")]
[MaxStringLength("1024")]
20: required string method;
[Description("Name of the assembly (dll, jar, etc.) containing this function.")]
[MaxStringLength("1024")]
30: string assembly;
[Description("File name or URL of the method implementation.")]
[MaxStringLength("1024")]
50: string fileName;
[Description("Line number of the code implementation.")]
60: int32 line;
}

View File

@@ -0,0 +1,60 @@
$generatorPath = "C:\src\mseng\AppInsights-Common"
$publicSchemaLocation = "https://raw.githubusercontent.com/Microsoft/ApplicationInsights-Home/master/EndpointSpecs/Schemas/Bond"
$currentDir = $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
#fix path
$generatorPath = "$generatorPath\..\bin\Debug\BondSchemaGenerator\BondSchemaGenerator"
#####################################################################
## PUBLIC SCHEMA
#####################################################################
mkdir -Force $currentDir\PublicSchema
del "$currentDir\PublicSchema\*.bond"
$argumentList = ""
# Download public schema from the github
@(
"AvailabilityData.bond",
"Base.bond",
"ContextTagKeys.bond",
"Data.bond",
"DataPoint.bond",
"DataPointType.bond",
"Domain.bond",
"Envelope.bond",
"EventData.bond",
"ExceptionData.bond",
"ExceptionDetails.bond",
"MessageData.bond",
"MetricData.bond",
"PageViewData.bond",
"RemoteDependencyData.bond",
"RequestData.bond",
"SeverityLevel.bond",
"StackFrame.bond"
) | ForEach-Object {
$fileName = $_
$argumentList = "$argumentList -i $currentDir\PublicSchema\$fileName"
& Invoke-WebRequest -o "$currentDir\PublicSchema\$fileName" "$publicSchemaLocation/$fileName"
}
$argumentList = "-v $argumentList -o $currentDir\PublicSchema\ -e TypeScriptLanguage -t TypeScriptLayout -n AI --flatten true"
# Generate public schema using bond generator
$p1 = Start-Process "$generatorPath\BondSchemaGenerator.exe" -ArgumentList $argumentList -wait -NoNewWindow -PassThru
$p1.HasExited
$p1.ExitCode
del "$currentDir\..\Declarations\Contracts\Generated\*.ts"
dir "$currentDir\PublicSchema\Contracts\Generated\*.ts" | ForEach-Object {
$fileName = $_
copy $fileName "$currentDir\..\Declarations\Contracts\Generated\"
}
del "$currentDir\PublicSchema\Contracts\Generated\*.ts"

View File

@@ -0,0 +1,226 @@
"use strict";
var CorrelationContextManager = require("./AutoCollection/CorrelationContextManager"); // Keep this first
var AutoCollectConsole = require("./AutoCollection/Console");
var AutoCollectExceptions = require("./AutoCollection/Exceptions");
var AutoCollectPerformance = require("./AutoCollection/Performance");
var AutoCollectClientRequests = require("./AutoCollection/ClientRequests");
var AutoCollectServerRequests = require("./AutoCollection/ServerRequests");
var Client = require("./Library/Client");
var Logging = require("./Library/Logging");
/**
* The singleton meta class for the default client of the client. This class is used to setup/start and configure
* the auto-collection behavior of the application insights module.
*/
var ApplicationInsights = (function () {
function ApplicationInsights() {
}
/**
* Initializes a client with the given instrumentation key, if this is not specified, the value will be
* read from the environment variable APPINSIGHTS_INSTRUMENTATIONKEY
* @returns {ApplicationInsights/Client} a new client
*/
ApplicationInsights.getClient = function (instrumentationKey) {
return new Client(instrumentationKey);
};
/**
* Initializes the default client of the client and sets the default configuration
* @param instrumentationKey the instrumentation key to use. Optional, if this is not specified, the value will be
* read from the environment variable APPINSIGHTS_INSTRUMENTATIONKEY
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setup = function (instrumentationKey) {
if (!ApplicationInsights.client) {
ApplicationInsights.client = ApplicationInsights.getClient(instrumentationKey);
ApplicationInsights._console = new AutoCollectConsole(ApplicationInsights.client);
ApplicationInsights._exceptions = new AutoCollectExceptions(ApplicationInsights.client);
ApplicationInsights._performance = new AutoCollectPerformance(ApplicationInsights.client);
ApplicationInsights._serverRequests = new AutoCollectServerRequests(ApplicationInsights.client);
ApplicationInsights._clientRequests = new AutoCollectClientRequests(ApplicationInsights.client);
}
else {
Logging.info("The default client is already setup");
}
if (ApplicationInsights.client && ApplicationInsights.client.channel) {
ApplicationInsights.client.channel.setOfflineMode(ApplicationInsights._isOfflineMode);
}
return ApplicationInsights;
};
/**
* Starts automatic collection of telemetry. Prior to calling start no telemetry will be collected
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.start = function () {
if (!!this.client) {
ApplicationInsights._isStarted = true;
ApplicationInsights._console.enable(ApplicationInsights._isConsole);
ApplicationInsights._exceptions.enable(ApplicationInsights._isExceptions);
ApplicationInsights._performance.enable(ApplicationInsights._isPerformance);
ApplicationInsights._serverRequests.useAutoCorrelation(ApplicationInsights._isCorrelating);
ApplicationInsights._serverRequests.enable(ApplicationInsights._isRequests);
ApplicationInsights._clientRequests.enable(ApplicationInsights._isDependencies);
}
else {
Logging.warn("Start cannot be called before setup");
}
return ApplicationInsights;
};
/**
* Returns an object that is shared across all code handling a given request. This can be used similarly to thread-local storage in other languages.
* Properties set on this object will be available to telemetry processors.
*
* Do not store sensitive information here.
* Custom properties set on this object can be exposed in a future SDK release via outgoing HTTP headers.
* This is to allow for correlating data cross-component.
*
* This method will return null if automatic dependency correlation is disabled.
* @returns A plain object for request storage or null if automatic dependency correlation is disabled.
*/
ApplicationInsights.getCorrelationContext = function () {
if (this._isCorrelating) {
return CorrelationContextManager.CorrelationContextManager.getCurrentContext();
}
return null;
};
/**
* Returns a function that will get the same correlation context within its function body as the code executing this function.
* Use this method if automatic dependency correlation is not propagating correctly to an asynchronous callback.
*/
ApplicationInsights.wrapWithCorrelationContext = function (fn) {
return CorrelationContextManager.CorrelationContextManager.wrapCallback(fn);
};
/**
* Sets the state of console tracking (enabled by default)
* @param value if true console activity will be sent to Application Insights
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setAutoCollectConsole = function (value) {
ApplicationInsights._isConsole = value;
if (ApplicationInsights._isStarted) {
ApplicationInsights._console.enable(value);
}
return ApplicationInsights;
};
/**
* Sets the state of exception tracking (enabled by default)
* @param value if true uncaught exceptions will be sent to Application Insights
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setAutoCollectExceptions = function (value) {
ApplicationInsights._isExceptions = value;
if (ApplicationInsights._isStarted) {
ApplicationInsights._exceptions.enable(value);
}
return ApplicationInsights;
};
/**
* Sets the state of performance tracking (enabled by default)
* @param value if true performance counters will be collected every second and sent to Application Insights
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setAutoCollectPerformance = function (value) {
ApplicationInsights._isPerformance = value;
if (ApplicationInsights._isStarted) {
ApplicationInsights._performance.enable(value);
}
return ApplicationInsights;
};
/**
* Sets the state of request tracking (enabled by default)
* @param value if true requests will be sent to Application Insights
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setAutoCollectRequests = function (value) {
ApplicationInsights._isRequests = value;
if (ApplicationInsights._isStarted) {
ApplicationInsights._serverRequests.enable(value);
}
return ApplicationInsights;
};
/**
* Sets the state of dependency tracking (enabled by default)
* @param value if true dependencies will be sent to Application Insights
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setAutoCollectDependencies = function (value) {
ApplicationInsights._isDependencies = value;
if (ApplicationInsights._isStarted) {
ApplicationInsights._clientRequests.enable(value);
}
return ApplicationInsights;
};
/**
* Sets the state of automatic dependency correlation (enabled by default)
* @param value if true dependencies will be correlated with requests
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setAutoDependencyCorrelation = function (value) {
ApplicationInsights._isCorrelating = value;
if (ApplicationInsights._isStarted) {
ApplicationInsights._serverRequests.useAutoCorrelation(value);
}
return ApplicationInsights;
};
/**
* Enable or disable offline mode to cache events when client is offline (disabled by default)
* @param value if true events that occured while client is offline will be cached on disk
* @param resendInterval. The wait interval for resending cached events.
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.setOfflineMode = function (value, resendInterval) {
ApplicationInsights._isOfflineMode = value;
if (ApplicationInsights.client && ApplicationInsights.client.channel) {
ApplicationInsights.client.channel.setOfflineMode(value, resendInterval);
}
return ApplicationInsights;
};
/**
* Enables verbose debug logging
* @returns {ApplicationInsights} this class
*/
ApplicationInsights.enableVerboseLogging = function (enableWarningLogging) {
if (enableWarningLogging === void 0) { enableWarningLogging = true; }
Logging.enableDebug = true;
Logging.disableWarnings = !enableWarningLogging;
return ApplicationInsights;
};
/**
* Disables verbose debug and warning logging
*/
ApplicationInsights.disableConsoleLogging = function () {
Logging.enableDebug = false;
Logging.disableWarnings = true;
return ApplicationInsights;
};
/**
* Disposes the default client and all the auto collectors so they can be reinitialized with different configuration
*/
ApplicationInsights.dispose = function () {
ApplicationInsights.client = null;
ApplicationInsights._isStarted = false;
if (ApplicationInsights._console) {
ApplicationInsights._console.dispose();
}
if (ApplicationInsights._exceptions) {
ApplicationInsights._exceptions.dispose();
}
if (ApplicationInsights._performance) {
ApplicationInsights._performance.dispose();
}
if (ApplicationInsights._serverRequests) {
ApplicationInsights._serverRequests.dispose();
}
if (ApplicationInsights._clientRequests) {
ApplicationInsights._clientRequests.dispose();
}
};
ApplicationInsights._isConsole = true;
ApplicationInsights._isExceptions = true;
ApplicationInsights._isPerformance = true;
ApplicationInsights._isRequests = true;
ApplicationInsights._isDependencies = true;
ApplicationInsights._isOfflineMode = false;
ApplicationInsights._isCorrelating = false;
ApplicationInsights._isStarted = false;
return ApplicationInsights;
}());
module.exports = ApplicationInsights;

View File

@@ -0,0 +1,88 @@
{
"_args": [
[
"applicationinsights@0.19.0",
"D:\\code\\vscode-code-runner"
]
],
"_from": "applicationinsights@0.19.0",
"_id": "applicationinsights@0.19.0",
"_inBundle": false,
"_integrity": "sha1-izrz1N8FQpwSfNZEMcuB5AYJfYM=",
"_location": "/applicationinsights",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "applicationinsights@0.19.0",
"name": "applicationinsights",
"escapedName": "applicationinsights",
"rawSpec": "0.19.0",
"saveSpec": null,
"fetchSpec": "0.19.0"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.19.0.tgz",
"_spec": "0.19.0",
"_where": "D:\\code\\vscode-code-runner",
"bugs": {
"url": "https://github.com/Microsoft/ApplicationInsights-node.js/issues"
},
"contributors": [
{
"name": "Application Insights Developer Support",
"email": "aidevsupport@microsoft.com"
},
{
"name": "kszostak",
"email": "kszostak@microsoft.com"
},
{
"name": "southwood",
"url": "https://github.com/southwood"
},
{
"name": "bogdanbe",
"email": "bogdanbe@microsoft.com"
},
{
"name": "lukim",
"email": "lukim@microsoft.com"
}
],
"dependencies": {
"zone.js": "0.7.6"
},
"description": "Microsoft Application Insights module for Node.JS",
"devDependencies": {
"mocha": "3.1.2",
"node-mocks-http": "1.2.3",
"sinon": "1.17.6",
"typescript": "2.0.10",
"typings": "2.0.0"
},
"homepage": "https://github.com/Microsoft/ApplicationInsights-node.js#readme",
"keywords": [
"exception monitoring",
"request monitoring",
"performance monitoring",
"application insights",
"microsoft",
"azure"
],
"license": "MIT",
"main": "applicationinsights",
"name": "applicationinsights",
"repository": {
"type": "git",
"url": "git+https://github.com/Microsoft/ApplicationInsights-node.js.git"
},
"scripts": {
"prepublish": "tsc --module commonjs --declaration applicationinsights.ts",
"pretest": "find Tests -type f -name \"*.ts\" | xargs tsc --module commonjs",
"test": "./node_modules/mocha/bin/mocha ./Tests --recursive"
},
"version": "0.19.0"
}

View File

@@ -0,0 +1,7 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true
}
}

View File

@@ -0,0 +1,7 @@
{
"globalDependencies": {
"mocha": "registry:dt/mocha#2.2.5+20161028141524",
"node": "registry:dt/node#0.10.1+20161019125345",
"sinon": "registry:dt/sinon#1.16.0+20160924120326"
}
}

View File

@@ -0,0 +1,8 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7155ef2d2dced331b5e389363a4180957e76f95e/mocha/mocha.d.ts",
"raw": "registry:dt/mocha#2.2.5+20161028141524",
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7155ef2d2dced331b5e389363a4180957e76f95e/mocha/mocha.d.ts"
}
}

View File

@@ -0,0 +1,8 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/988a48ab2cfff3243868d70d836332a118d9f060/node/node-0.10.d.ts",
"raw": "registry:dt/node#0.10.1+20161019125345",
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/988a48ab2cfff3243868d70d836332a118d9f060/node/node-0.10.d.ts"
}
}

View File

@@ -0,0 +1,8 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/4bb4c29cad26ee049899b35da7d9502c3c73fde6/sinon/sinon.d.ts",
"raw": "registry:dt/sinon#1.16.0+20160924120326",
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/4bb4c29cad26ee049899b35da7d9502c3c73fde6/sinon/sinon.d.ts"
}
}

View File

@@ -0,0 +1,184 @@
# Release history
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
<details>
<summary><strong>Guiding Principles</strong></summary>
- Changelogs are for humans, not machines.
- There should be an entry for every single version.
- The same types of changes should be grouped.
- Versions and sections should be linkable.
- The latest version comes first.
- The release date of each versions is displayed.
- Mention whether you follow Semantic Versioning.
</details>
<details>
<summary><strong>Types of changes</strong></summary>
Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_):
- `Added` for new features.
- `Changed` for changes in existing functionality.
- `Deprecated` for soon-to-be removed features.
- `Removed` for now removed features.
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.
</details>
## [3.0.0] - 2018-04-08
v3.0 is a complete refactor, resulting in a faster, smaller codebase, with fewer deps, and a more accurate parser and compiler.
**Breaking Changes**
- The undocumented `.makeRe` method was removed
**Non-breaking changes**
- Caching was removed
## [2.3.2] - 2018-04-08
- start refactoring
- cover sets
- better range handling
## [2.3.1] - 2018-02-17
- Remove unnecessary escape in Regex. (#14)
## [2.3.0] - 2017-10-19
- minor code reorganization
- optimize regex
- expose `maxLength` option
## [2.2.1] - 2017-05-30
- don't condense when braces contain extglobs
## [2.2.0] - 2017-05-28
- ensure word boundaries are preserved
- fixes edge case where extglob characters precede a brace pattern
## [2.1.1] - 2017-04-27
- use snapdragon-node
- handle edge case
- optimizations, lint
## [2.0.4] - 2017-04-11
- pass opts to compiler
- minor optimization in create method
- re-write parser handlers to remove negation regex
## [2.0.3] - 2016-12-10
- use split-string
- clear queue at the end
- adds sequences example
- add unit tests
## [2.0.2] - 2016-10-21
- fix comma handling in nested extglobs
## [2.0.1] - 2016-10-20
- add comments
- more tests, ensure quotes are stripped
## [2.0.0] - 2016-10-19
- don't expand braces inside character classes
- add quantifier pattern
## [1.8.5] - 2016-05-21
- Refactor (#10)
## [1.8.4] - 2016-04-20
- fixes https://github.com/jonschlinkert/micromatch/issues/66
## [1.8.0] - 2015-03-18
- adds exponent examples, tests
- fixes the first example in https://github.com/jonschlinkert/micromatch/issues/38
## [1.6.0] - 2015-01-30
- optimizations, `bash` mode:
- improve path escaping
## [1.5.0] - 2015-01-28
- Merge pull request #5 from eush77/lib-files
## [1.4.0] - 2015-01-24
- add extglob tests
- externalize exponent function
- better whitespace handling
## [1.3.0] - 2015-01-24
- make regex patterns explicity
## [1.1.0] - 2015-01-11
- don't create a match group with `makeRe`
## [1.0.0] - 2014-12-23
- Merge commit '97b05f5544f8348736a8efaecf5c32bbe3e2ad6e'
- support empty brace syntax
- better bash coverage
- better support for regex strings
## [0.1.4] - 2014-11-14
- improve recognition of bad args, recognize mismatched argument types
- support escaping
- remove pathname-expansion
- support whitespace in patterns
## [0.1.0]
- first commit
[2.3.2]: https://github.com/micromatch/braces/compare/2.3.1...2.3.2
[2.3.1]: https://github.com/micromatch/braces/compare/2.3.0...2.3.1
[2.3.0]: https://github.com/micromatch/braces/compare/2.2.1...2.3.0
[2.2.1]: https://github.com/micromatch/braces/compare/2.2.0...2.2.1
[2.2.0]: https://github.com/micromatch/braces/compare/2.1.1...2.2.0
[2.1.1]: https://github.com/micromatch/braces/compare/2.1.0...2.1.1
[2.1.0]: https://github.com/micromatch/braces/compare/2.0.4...2.1.0
[2.0.4]: https://github.com/micromatch/braces/compare/2.0.3...2.0.4
[2.0.3]: https://github.com/micromatch/braces/compare/2.0.2...2.0.3
[2.0.2]: https://github.com/micromatch/braces/compare/2.0.1...2.0.2
[2.0.1]: https://github.com/micromatch/braces/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/micromatch/braces/compare/1.8.5...2.0.0
[1.8.5]: https://github.com/micromatch/braces/compare/1.8.4...1.8.5
[1.8.4]: https://github.com/micromatch/braces/compare/1.8.0...1.8.4
[1.8.0]: https://github.com/micromatch/braces/compare/1.6.0...1.8.0
[1.6.0]: https://github.com/micromatch/braces/compare/1.5.0...1.6.0
[1.5.0]: https://github.com/micromatch/braces/compare/1.4.0...1.5.0
[1.4.0]: https://github.com/micromatch/braces/compare/1.3.0...1.4.0
[1.3.0]: https://github.com/micromatch/braces/compare/1.2.0...1.3.0
[1.2.0]: https://github.com/micromatch/braces/compare/1.1.0...1.2.0
[1.1.0]: https://github.com/micromatch/braces/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/micromatch/braces/compare/0.1.4...1.0.0
[0.1.4]: https://github.com/micromatch/braces/compare/0.1.0...0.1.4
[Unreleased]: https://github.com/micromatch/braces/compare/0.1.0...HEAD
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2018, Jon Schlinkert.
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.

View File

@@ -0,0 +1,593 @@
# braces [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces) [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/micromatch/braces.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/braces)
> Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save braces
```
## v3.0.0 Released!!
See the [changelog](CHANGELOG.md) for details.
## Why use braces?
Brace patterns make globs more powerful by adding the ability to match specific ranges and sequences of characters.
* **Accurate** - complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
* **[fast and performant](#benchmarks)** - Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
* **Organized code base** - The parser and compiler are easy to maintain and update when edge cases crop up.
* **Well-tested** - Thousands of test assertions, and passes all of the Bash, minimatch, and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests (as of the date this was written).
* **Safer** - You shouldn't have to worry about users defining aggressive or malicious brace patterns that can break your application. Braces takes measures to prevent malicious regex that can be used for DDoS attacks (see [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html)).
* [Supports lists](#lists) - (aka "sets") `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
* [Supports sequences](#sequences) - (aka "ranges") `{01..03}` => `['01', '02', '03']`
* [Supports steps](#steps) - (aka "increments") `{2..10..2}` => `['2', '4', '6', '8', '10']`
* [Supports escaping](#escaping) - To prevent evaluation of special characters.
## Usage
The main export is a function that takes one or more brace `patterns` and `options`.
```js
const braces = require('braces');
// braces(patterns[, options]);
console.log(braces(['{01..05}', '{a..e}']));
//=> ['(0[1-5])', '([a-e])']
console.log(braces(['{01..05}', '{a..e}'], { expand: true }));
//=> ['01', '02', '03', '04', '05', 'a', 'b', 'c', 'd', 'e']
```
### Brace Expansion vs. Compilation
By default, brace patterns are compiled into strings that are optimized for creating regular expressions and matching.
**Compiled**
```js
console.log(braces('a/{x,y,z}/b'));
//=> ['a/(x|y|z)/b']
console.log(braces(['a/{01..20}/b', 'a/{1..5}/b']));
//=> [ 'a/(0[1-9]|1[0-9]|20)/b', 'a/([1-5])/b' ]
```
**Expanded**
Enable brace expansion by setting the `expand` option to true, or by using [braces.expand()](#expand) (returns an array similar to what you'd expect from Bash, or `echo {1..5}`, or [minimatch](https://github.com/isaacs/minimatch)):
```js
console.log(braces('a/{x,y,z}/b', { expand: true }));
//=> ['a/x/b', 'a/y/b', 'a/z/b']
console.log(braces.expand('{01..10}'));
//=> ['01','02','03','04','05','06','07','08','09','10']
```
### Lists
Expand lists (like Bash "sets"):
```js
console.log(braces('a/{foo,bar,baz}/*.js'));
//=> ['a/(foo|bar|baz)/*.js']
console.log(braces.expand('a/{foo,bar,baz}/*.js'));
//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
```
### Sequences
Expand ranges of characters (like Bash "sequences"):
```js
console.log(braces.expand('{1..3}')); // ['1', '2', '3']
console.log(braces.expand('a/{1..3}/b')); // ['a/1/b', 'a/2/b', 'a/3/b']
console.log(braces('{a..c}', { expand: true })); // ['a', 'b', 'c']
console.log(braces('foo/{a..c}', { expand: true })); // ['foo/a', 'foo/b', 'foo/c']
// supports zero-padded ranges
console.log(braces('a/{01..03}/b')); //=> ['a/(0[1-3])/b']
console.log(braces('a/{001..300}/b')); //=> ['a/(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)/b']
```
See [fill-range](https://github.com/jonschlinkert/fill-range) for all available range-expansion options.
### Steppped ranges
Steps, or increments, may be used with ranges:
```js
console.log(braces.expand('{2..10..2}'));
//=> ['2', '4', '6', '8', '10']
console.log(braces('{2..10..2}'));
//=> ['(2|4|6|8|10)']
```
When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range](https://github.com/jonschlinkert/to-regex-range) for expansion.
### Nesting
Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.
**"Expanded" braces**
```js
console.log(braces.expand('a{b,c,/{x,y}}/e'));
//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']
console.log(braces.expand('a/{x,{1..5},y}/c'));
//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
```
**"Optimized" braces**
```js
console.log(braces('a{b,c,/{x,y}}/e'));
//=> ['a(b|c|/(x|y))/e']
console.log(braces('a/{x,{1..5},y}/c'));
//=> ['a/(x|([1-5])|y)/c']
```
### Escaping
**Escaping braces**
A brace pattern will not be expanded or evaluted if _either the opening or closing brace is escaped_:
```js
console.log(braces.expand('a\\{d,c,b}e'));
//=> ['a{d,c,b}e']
console.log(braces.expand('a{d,c,b\\}e'));
//=> ['a{d,c,b}e']
```
**Escaping commas**
Commas inside braces may also be escaped:
```js
console.log(braces.expand('a{b\\,c}d'));
//=> ['a{b,c}d']
console.log(braces.expand('a{d\\,c,b}e'));
//=> ['ad,ce', 'abe']
```
**Single items**
Following bash conventions, a brace pattern is also not expanded when it contains a single character:
```js
console.log(braces.expand('a{b}c'));
//=> ['a{b}c']
```
## Options
### options.maxLength
**Type**: `Number`
**Default**: `65,536`
**Description**: Limit the length of the input string. Useful when the input string is generated or your application allows users to pass a string, et cetera.
```js
console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
```
### options.expand
**Type**: `Boolean`
**Default**: `undefined`
**Description**: Generate an "expanded" brace pattern (alternatively you can use the `braces.expand()` method, which does the same thing).
```js
console.log(braces('a/{b,c}/d', { expand: true }));
//=> [ 'a/b/d', 'a/c/d' ]
```
### options.nodupes
**Type**: `Boolean`
**Default**: `undefined`
**Description**: Remove duplicates from the returned array.
### options.rangeLimit
**Type**: `Number`
**Default**: `1000`
**Description**: To prevent malicious patterns from being passed by users, an error is thrown when `braces.expand()` is used or `options.expand` is true and the generated range will exceed the `rangeLimit`.
You can customize `options.rangeLimit` or set it to `Inifinity` to disable this altogether.
**Examples**
```js
// pattern exceeds the "rangeLimit", so it's optimized automatically
console.log(braces.expand('{1..1000}'));
//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']
// pattern does not exceed "rangeLimit", so it's NOT optimized
console.log(braces.expand('{1..100}'));
//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100']
```
### options.transform
**Type**: `Function`
**Default**: `undefined`
**Description**: Customize range expansion.
**Example: Transforming non-numeric values**
```js
const alpha = braces.expand('x/{a..e}/y', {
transform(value, index) {
// When non-numeric values are passed, "value" is a character code.
return 'foo/' + String.fromCharCode(value) + '-' + index;
}
});
console.log(alpha);
//=> [ 'x/foo/a-0/y', 'x/foo/b-1/y', 'x/foo/c-2/y', 'x/foo/d-3/y', 'x/foo/e-4/y' ]
```
**Example: Transforming numeric values**
```js
const numeric = braces.expand('{1..5}', {
transform(value) {
// when numeric values are passed, "value" is a number
return 'foo/' + value * 2;
}
});
console.log(numeric);
//=> [ 'foo/2', 'foo/4', 'foo/6', 'foo/8', 'foo/10' ]
```
### options.quantifiers
**Type**: `Boolean`
**Default**: `undefined`
**Description**: In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.
**Examples**
```js
const braces = require('braces');
console.log(braces('a/b{1,3}/{x,y,z}'));
//=> [ 'a/b(1|3)/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
//=> [ 'a/b{1,3}/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
```
### options.unescape
**Type**: `Boolean`
**Default**: `undefined`
**Description**: Strip backslashes that were used for escaping from the result.
## What is "brace expansion"?
Brace expansion is a type of parameter expansion that was made popular by unix shells for generating lists of strings, as well as regex-like matching when used alongside wildcards (globs).
In addition to "expansion", braces are also used for matching. In other words:
* [brace expansion](#brace-expansion) is for generating new lists
* [brace matching](#brace-matching) is for filtering existing lists
<details>
<summary><strong>More about brace expansion</strong> (click to expand)</summary>
There are two main types of brace expansion:
1. **lists**: which are defined using comma-separated values inside curly braces: `{a,b,c}`
2. **sequences**: which are defined using a starting value and an ending value, separated by two dots: `a{1..3}b`. Optionally, a third argument may be passed to define a "step" or increment to use: `a{1..100..10}b`. These are also sometimes referred to as "ranges".
Here are some example brace patterns to illustrate how they work:
**Sets**
```
{a,b,c} => a b c
{a,b,c}{1,2} => a1 a2 b1 b2 c1 c2
```
**Sequences**
```
{1..9} => 1 2 3 4 5 6 7 8 9
{4..-4} => 4 3 2 1 0 -1 -2 -3 -4
{1..20..3} => 1 4 7 10 13 16 19
{a..j} => a b c d e f g h i j
{j..a} => j i h g f e d c b a
{a..z..3} => a d g j m p s v y
```
**Combination**
Sets and sequences can be mixed together or used along with any other strings.
```
{a,b,c}{1..3} => a1 a2 a3 b1 b2 b3 c1 c2 c3
foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
```
The fact that braces can be "expanded" from relatively simple patterns makes them ideal for quickly generating test fixtures, file paths, and similar use cases.
## Brace matching
In addition to _expansion_, brace patterns are also useful for performing regular-expression-like matching.
For example, the pattern `foo/{1..3}/bar` would match any of following strings:
```
foo/1/bar
foo/2/bar
foo/3/bar
```
But not:
```
baz/1/qux
baz/2/qux
baz/3/qux
```
Braces can also be combined with [glob patterns](https://github.com/jonschlinkert/micromatch) to perform more advanced wildcard matching. For example, the pattern `*/{1..3}/*` would match any of following strings:
```
foo/1/bar
foo/2/bar
foo/3/bar
baz/1/qux
baz/2/qux
baz/3/qux
```
## Brace matching pitfalls
Although brace patterns offer a user-friendly way of matching ranges or sets of strings, there are also some major disadvantages and potential risks you should be aware of.
### tldr
**"brace bombs"**
* brace expansion can eat up a huge amount of processing resources
* as brace patterns increase _linearly in size_, the system resources required to expand the pattern increase exponentially
* users can accidentally (or intentially) exhaust your system's resources resulting in the equivalent of a DoS attack (bonus: no programming knowledge is required!)
For a more detailed explanation with examples, see the [geometric complexity](#geometric-complexity) section.
### The solution
Jump to the [performance section](#performance) to see how Braces solves this problem in comparison to other libraries.
### Geometric complexity
At minimum, brace patterns with sets limited to two elements have quadradic or `O(n^2)` complexity. But the complexity of the algorithm increases exponentially as the number of sets, _and elements per set_, increases, which is `O(n^c)`.
For example, the following sets demonstrate quadratic (`O(n^2)`) complexity:
```
{1,2}{3,4} => (2X2) => 13 14 23 24
{1,2}{3,4}{5,6} => (2X2X2) => 135 136 145 146 235 236 245 246
```
But add an element to a set, and we get a n-fold Cartesian product with `O(n^c)` complexity:
```
{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248
249 257 258 259 267 268 269 347 348 349 357
358 359 367 368 369
```
Now, imagine how this complexity grows given that each element is a n-tuple:
```
{1..100}{1..100} => (100X100) => 10,000 elements (38.4 kB)
{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
```
Although these examples are clearly contrived, they demonstrate how brace patterns can quickly grow out of control.
**More information**
Interested in learning more about brace expansion?
* [linuxjournal/bash-brace-expansion](http://www.linuxjournal.com/content/bash-brace-expansion)
* [rosettacode/Brace_expansion](https://rosettacode.org/wiki/Brace_expansion)
* [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)
</details>
## Performance
Braces is not only screaming fast, it's also more accurate the other brace expansion libraries.
### Better algorithms
Fortunately there is a solution to the ["brace bomb" problem](#brace-matching-pitfalls): _don't expand brace patterns into an array when they're used for matching_.
Instead, convert the pattern into an optimized regular expression. This is easier said than done, and braces is the only library that does this currently.
**The proof is in the numbers**
Minimatch gets exponentially slower as patterns increase in complexity, braces does not. The following results were generated using `braces()` and `minimatch.braceExpand()`, respectively.
| **Pattern** | **braces** | **[minimatch][]** |
| --- | --- | --- |
| `{1..9007199254740991}`[^1] | `298 B` (5ms 459μs)| N/A (freezes) |
| `{1..1000000000000000}` | `41 B` (1ms 15μs) | N/A (freezes) |
| `{1..100000000000000}` | `40 B` (890μs) | N/A (freezes) |
| `{1..10000000000000}` | `39 B` (2ms 49μs) | N/A (freezes) |
| `{1..1000000000000}` | `38 B` (608μs) | N/A (freezes) |
| `{1..100000000000}` | `37 B` (397μs) | N/A (freezes) |
| `{1..10000000000}` | `35 B` (983μs) | N/A (freezes) |
| `{1..1000000000}` | `34 B` (798μs) | N/A (freezes) |
| `{1..100000000}` | `33 B` (733μs) | N/A (freezes) |
| `{1..10000000}` | `32 B` (5ms 632μs) | `78.89 MB` (16s 388ms 569μs) |
| `{1..1000000}` | `31 B` (1ms 381μs) | `6.89 MB` (1s 496ms 887μs) |
| `{1..100000}` | `30 B` (950μs) | `588.89 kB` (146ms 921μs) |
| `{1..10000}` | `29 B` (1ms 114μs) | `48.89 kB` (14ms 187μs) |
| `{1..1000}` | `28 B` (760μs) | `3.89 kB` (1ms 453μs) |
| `{1..100}` | `22 B` (345μs) | `291 B` (196μs) |
| `{1..10}` | `10 B` (533μs) | `20 B` (37μs) |
| `{1..3}` | `7 B` (190μs) | `5 B` (27μs) |
### Faster algorithms
When you need expansion, braces is still much faster.
_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`, respectively)_
| **Pattern** | **braces** | **[minimatch][]** |
| --- | --- | --- |
| `{1..10000000}` | `78.89 MB` (2s 698ms 642μs) | `78.89 MB` (18s 601ms 974μs) |
| `{1..1000000}` | `6.89 MB` (458ms 576μs) | `6.89 MB` (1s 491ms 621μs) |
| `{1..100000}` | `588.89 kB` (20ms 728μs) | `588.89 kB` (156ms 919μs) |
| `{1..10000}` | `48.89 kB` (2ms 202μs) | `48.89 kB` (13ms 641μs) |
| `{1..1000}` | `3.89 kB` (1ms 796μs) | `3.89 kB` (1ms 958μs) |
| `{1..100}` | `291 B` (424μs) | `291 B` (211μs) |
| `{1..10}` | `20 B` (487μs) | `20 B` (72μs) |
| `{1..3}` | `5 B` (166μs) | `5 B` (27μs) |
If you'd like to run these comparisons yourself, see [test/support/generate.js](test/support/generate.js).
## Benchmarks
### Running benchmarks
Install dev dependencies:
```bash
npm i -d && npm benchmark
```
### Latest results
Braces is more accurate, without sacrificing performance.
```bash
# range (expanded)
braces x 29,040 ops/sec ±3.69% (91 runs sampled))
minimatch x 4,735 ops/sec ±1.28% (90 runs sampled)
# range (optimized for regex)
braces x 382,878 ops/sec ±0.56% (94 runs sampled)
minimatch x 1,040 ops/sec ±0.44% (93 runs sampled)
# nested ranges (expanded)
braces x 19,744 ops/sec ±2.27% (92 runs sampled))
minimatch x 4,579 ops/sec ±0.50% (93 runs sampled)
# nested ranges (optimized for regex)
braces x 246,019 ops/sec ±2.02% (93 runs sampled)
minimatch x 1,028 ops/sec ±0.39% (94 runs sampled)
# set (expanded)
braces x 138,641 ops/sec ±0.53% (95 runs sampled)
minimatch x 219,582 ops/sec ±0.98% (94 runs sampled)
# set (optimized for regex)
braces x 388,408 ops/sec ±0.41% (95 runs sampled)
minimatch x 44,724 ops/sec ±0.91% (89 runs sampled)
# nested sets (expanded)
braces x 84,966 ops/sec ±0.48% (94 runs sampled)
minimatch x 140,720 ops/sec ±0.37% (95 runs sampled)
# nested sets (optimized for regex)
braces x 263,340 ops/sec ±2.06% (92 runs sampled)
minimatch x 28,714 ops/sec ±0.40% (90 runs sampled)
```
## 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).
</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>
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 197 | [jonschlinkert](https://github.com/jonschlinkert) |
| 4 | [doowb](https://github.com/doowb) |
| 1 | [es128](https://github.com/es128) |
| 1 | [eush77](https://github.com/eush77) |
| 1 | [hemanth](https://github.com/hemanth) |
| 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
### Author
**Jon Schlinkert**
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
### License
Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 08, 2019._

View File

@@ -0,0 +1,170 @@
'use strict';
const stringify = require('./lib/stringify');
const compile = require('./lib/compile');
const expand = require('./lib/expand');
const parse = require('./lib/parse');
/**
* Expand the given pattern or create a regex-compatible string.
*
* ```js
* const braces = require('braces');
* console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)']
* console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c']
* ```
* @param {String} `str`
* @param {Object} `options`
* @return {String}
* @api public
*/
const braces = (input, options = {}) => {
let output = [];
if (Array.isArray(input)) {
for (let pattern of input) {
let result = braces.create(pattern, options);
if (Array.isArray(result)) {
output.push(...result);
} else {
output.push(result);
}
}
} else {
output = [].concat(braces.create(input, options));
}
if (options && options.expand === true && options.nodupes === true) {
output = [...new Set(output)];
}
return output;
};
/**
* Parse the given `str` with the given `options`.
*
* ```js
* // braces.parse(pattern, [, options]);
* const ast = braces.parse('a/{b,c}/d');
* console.log(ast);
* ```
* @param {String} pattern Brace pattern to parse
* @param {Object} options
* @return {Object} Returns an AST
* @api public
*/
braces.parse = (input, options = {}) => parse(input, options);
/**
* Creates a braces string from an AST, or an AST node.
*
* ```js
* const braces = require('braces');
* let ast = braces.parse('foo/{a,b}/bar');
* console.log(stringify(ast.nodes[2])); //=> '{a,b}'
* ```
* @param {String} `input` Brace pattern or AST.
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/
braces.stringify = (input, options = {}) => {
if (typeof input === 'string') {
return stringify(braces.parse(input, options), options);
}
return stringify(input, options);
};
/**
* Compiles a brace pattern into a regex-compatible, optimized string.
* This method is called by the main [braces](#braces) function by default.
*
* ```js
* const braces = require('braces');
* console.log(braces.compile('a/{b,c}/d'));
* //=> ['a/(b|c)/d']
* ```
* @param {String} `input` Brace pattern or AST.
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/
braces.compile = (input, options = {}) => {
if (typeof input === 'string') {
input = braces.parse(input, options);
}
return compile(input, options);
};
/**
* Expands a brace pattern into an array. This method is called by the
* main [braces](#braces) function when `options.expand` is true. Before
* using this method it's recommended that you read the [performance notes](#performance))
* and advantages of using [.compile](#compile) instead.
*
* ```js
* const braces = require('braces');
* console.log(braces.expand('a/{b,c}/d'));
* //=> ['a/b/d', 'a/c/d'];
* ```
* @param {String} `pattern` Brace pattern
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/
braces.expand = (input, options = {}) => {
if (typeof input === 'string') {
input = braces.parse(input, options);
}
let result = expand(input, options);
// filter out empty strings if specified
if (options.noempty === true) {
result = result.filter(Boolean);
}
// filter out duplicates if specified
if (options.nodupes === true) {
result = [...new Set(result)];
}
return result;
};
/**
* Processes a brace pattern and returns either an expanded array
* (if `options.expand` is true), a highly optimized regex-compatible string.
* This method is called by the main [braces](#braces) function.
*
* ```js
* const braces = require('braces');
* console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
* //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
* ```
* @param {String} `pattern` Brace pattern
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/
braces.create = (input, options = {}) => {
if (input === '' || input.length < 3) {
return [input];
}
return options.expand !== true
? braces.compile(input, options)
: braces.expand(input, options);
};
/**
* Expose "braces"
*/
module.exports = braces;

View File

@@ -0,0 +1,57 @@
'use strict';
const fill = require('fill-range');
const utils = require('./utils');
const compile = (ast, options = {}) => {
let walk = (node, parent = {}) => {
let invalidBlock = utils.isInvalidBrace(parent);
let invalidNode = node.invalid === true && options.escapeInvalid === true;
let invalid = invalidBlock === true || invalidNode === true;
let prefix = options.escapeInvalid === true ? '\\' : '';
let output = '';
if (node.isOpen === true) {
return prefix + node.value;
}
if (node.isClose === true) {
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) {
let args = utils.reduce(node.nodes);
let range = fill(...args, { ...options, wrap: false, toRegex: true });
if (range.length !== 0) {
return args.length > 1 && range.length > 1 ? `(${range})` : range;
}
}
if (node.nodes) {
for (let child of node.nodes) {
output += walk(child, node);
}
}
return output;
};
return walk(ast);
};
module.exports = compile;

View File

@@ -0,0 +1,57 @@
'use strict';
module.exports = {
MAX_LENGTH: 1024 * 64,
// Digits
CHAR_0: '0', /* 0 */
CHAR_9: '9', /* 9 */
// Alphabet chars.
CHAR_UPPERCASE_A: 'A', /* A */
CHAR_LOWERCASE_A: 'a', /* a */
CHAR_UPPERCASE_Z: 'Z', /* Z */
CHAR_LOWERCASE_Z: 'z', /* z */
CHAR_LEFT_PARENTHESES: '(', /* ( */
CHAR_RIGHT_PARENTHESES: ')', /* ) */
CHAR_ASTERISK: '*', /* * */
// Non-alphabetic chars.
CHAR_AMPERSAND: '&', /* & */
CHAR_AT: '@', /* @ */
CHAR_BACKSLASH: '\\', /* \ */
CHAR_BACKTICK: '`', /* ` */
CHAR_CARRIAGE_RETURN: '\r', /* \r */
CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
CHAR_COLON: ':', /* : */
CHAR_COMMA: ',', /* , */
CHAR_DOLLAR: '$', /* . */
CHAR_DOT: '.', /* . */
CHAR_DOUBLE_QUOTE: '"', /* " */
CHAR_EQUAL: '=', /* = */
CHAR_EXCLAMATION_MARK: '!', /* ! */
CHAR_FORM_FEED: '\f', /* \f */
CHAR_FORWARD_SLASH: '/', /* / */
CHAR_HASH: '#', /* # */
CHAR_HYPHEN_MINUS: '-', /* - */
CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
CHAR_LEFT_CURLY_BRACE: '{', /* { */
CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
CHAR_LINE_FEED: '\n', /* \n */
CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
CHAR_PERCENT: '%', /* % */
CHAR_PLUS: '+', /* + */
CHAR_QUESTION_MARK: '?', /* ? */
CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
CHAR_RIGHT_CURLY_BRACE: '}', /* } */
CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */
CHAR_SEMICOLON: ';', /* ; */
CHAR_SINGLE_QUOTE: '\'', /* ' */
CHAR_SPACE: ' ', /* */
CHAR_TAB: '\t', /* \t */
CHAR_UNDERSCORE: '_', /* _ */
CHAR_VERTICAL_LINE: '|', /* | */
CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */
};

View File

@@ -0,0 +1,113 @@
'use strict';
const fill = require('fill-range');
const stringify = require('./stringify');
const utils = require('./utils');
const append = (queue = '', stash = '', enclose = false) => {
let result = [];
queue = [].concat(queue);
stash = [].concat(stash);
if (!stash.length) return queue;
if (!queue.length) {
return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash;
}
for (let item of queue) {
if (Array.isArray(item)) {
for (let value of item) {
result.push(append(value, stash, enclose));
}
} else {
for (let ele of stash) {
if (enclose === true && typeof ele === 'string') ele = `{${ele}}`;
result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele));
}
}
}
return utils.flatten(result);
};
const expand = (ast, options = {}) => {
let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit;
let walk = (node, parent = {}) => {
node.queue = [];
let p = parent;
let q = parent.queue;
while (p.type !== 'brace' && p.type !== 'root' && p.parent) {
p = p.parent;
q = p.queue;
}
if (node.invalid || node.dollar) {
q.push(append(q.pop(), stringify(node, options)));
return;
}
if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) {
q.push(append(q.pop(), ['{}']));
return;
}
if (node.nodes && node.ranges > 0) {
let args = utils.reduce(node.nodes);
if (utils.exceedsLimit(...args, options.step, rangeLimit)) {
throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
}
let range = fill(...args, options);
if (range.length === 0) {
range = stringify(node, options);
}
q.push(append(q.pop(), range));
node.nodes = [];
return;
}
let enclose = utils.encloseBrace(node);
let queue = node.queue;
let block = node;
while (block.type !== 'brace' && block.type !== 'root' && block.parent) {
block = block.parent;
queue = block.queue;
}
for (let i = 0; i < node.nodes.length; i++) {
let child = node.nodes[i];
if (child.type === 'comma' && node.type === 'brace') {
if (i === 1) queue.push('');
queue.push('');
continue;
}
if (child.type === 'close') {
q.push(append(q.pop(), queue, enclose));
continue;
}
if (child.value && child.type !== 'open') {
queue.push(append(queue.pop(), child.value));
continue;
}
if (child.nodes) {
walk(child, node);
}
}
return queue;
};
return utils.flatten(walk(ast));
};
module.exports = expand;

View File

@@ -0,0 +1,333 @@
'use strict';
const stringify = require('./stringify');
/**
* Constants
*/
const {
MAX_LENGTH,
CHAR_BACKSLASH, /* \ */
CHAR_BACKTICK, /* ` */
CHAR_COMMA, /* , */
CHAR_DOT, /* . */
CHAR_LEFT_PARENTHESES, /* ( */
CHAR_RIGHT_PARENTHESES, /* ) */
CHAR_LEFT_CURLY_BRACE, /* { */
CHAR_RIGHT_CURLY_BRACE, /* } */
CHAR_LEFT_SQUARE_BRACKET, /* [ */
CHAR_RIGHT_SQUARE_BRACKET, /* ] */
CHAR_DOUBLE_QUOTE, /* " */
CHAR_SINGLE_QUOTE, /* ' */
CHAR_NO_BREAK_SPACE,
CHAR_ZERO_WIDTH_NOBREAK_SPACE
} = require('./constants');
/**
* parse
*/
const parse = (input, options = {}) => {
if (typeof input !== 'string') {
throw new TypeError('Expected a string');
}
let opts = options || {};
let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
if (input.length > max) {
throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
}
let ast = { type: 'root', input, nodes: [] };
let stack = [ast];
let block = ast;
let prev = ast;
let brackets = 0;
let length = input.length;
let index = 0;
let depth = 0;
let value;
let memo = {};
/**
* Helpers
*/
const advance = () => input[index++];
const push = node => {
if (node.type === 'text' && prev.type === 'dot') {
prev.type = 'text';
}
if (prev && prev.type === 'text' && node.type === 'text') {
prev.value += node.value;
return;
}
block.nodes.push(node);
node.parent = block;
node.prev = prev;
prev = node;
return node;
};
push({ type: 'bos' });
while (index < length) {
block = stack[stack.length - 1];
value = advance();
/**
* Invalid chars
*/
if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) {
continue;
}
/**
* Escaped chars
*/
if (value === CHAR_BACKSLASH) {
push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() });
continue;
}
/**
* Right square bracket (literal): ']'
*/
if (value === CHAR_RIGHT_SQUARE_BRACKET) {
push({ type: 'text', value: '\\' + value });
continue;
}
/**
* Left square bracket: '['
*/
if (value === CHAR_LEFT_SQUARE_BRACKET) {
brackets++;
let closed = true;
let next;
while (index < length && (next = advance())) {
value += next;
if (next === CHAR_LEFT_SQUARE_BRACKET) {
brackets++;
continue;
}
if (next === CHAR_BACKSLASH) {
value += advance();
continue;
}
if (next === CHAR_RIGHT_SQUARE_BRACKET) {
brackets--;
if (brackets === 0) {
break;
}
}
}
push({ type: 'text', value });
continue;
}
/**
* Parentheses
*/
if (value === CHAR_LEFT_PARENTHESES) {
block = push({ type: 'paren', nodes: [] });
stack.push(block);
push({ type: 'text', value });
continue;
}
if (value === CHAR_RIGHT_PARENTHESES) {
if (block.type !== 'paren') {
push({ type: 'text', value });
continue;
}
block = stack.pop();
push({ type: 'text', value });
block = stack[stack.length - 1];
continue;
}
/**
* Quotes: '|"|`
*/
if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) {
let open = value;
let next;
if (options.keepQuotes !== true) {
value = '';
}
while (index < length && (next = advance())) {
if (next === CHAR_BACKSLASH) {
value += next + advance();
continue;
}
if (next === open) {
if (options.keepQuotes === true) value += next;
break;
}
value += next;
}
push({ type: 'text', value });
continue;
}
/**
* Left curly brace: '{'
*/
if (value === CHAR_LEFT_CURLY_BRACE) {
depth++;
let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
let brace = {
type: 'brace',
open: true,
close: false,
dollar,
depth,
commas: 0,
ranges: 0,
nodes: []
};
block = push(brace);
stack.push(block);
push({ type: 'open', value });
continue;
}
/**
* Right curly brace: '}'
*/
if (value === CHAR_RIGHT_CURLY_BRACE) {
if (block.type !== 'brace') {
push({ type: 'text', value });
continue;
}
let type = 'close';
block = stack.pop();
block.close = true;
push({ type, value });
depth--;
block = stack[stack.length - 1];
continue;
}
/**
* Comma: ','
*/
if (value === CHAR_COMMA && depth > 0) {
if (block.ranges > 0) {
block.ranges = 0;
let open = block.nodes.shift();
block.nodes = [open, { type: 'text', value: stringify(block) }];
}
push({ type: 'comma', value });
block.commas++;
continue;
}
/**
* Dot: '.'
*/
if (value === CHAR_DOT && depth > 0 && block.commas === 0) {
let siblings = block.nodes;
if (depth === 0 || siblings.length === 0) {
push({ type: 'text', value });
continue;
}
if (prev.type === 'dot') {
block.range = [];
prev.value += value;
prev.type = 'range';
if (block.nodes.length !== 3 && block.nodes.length !== 5) {
block.invalid = true;
block.ranges = 0;
prev.type = 'text';
continue;
}
block.ranges++;
block.args = [];
continue;
}
if (prev.type === 'range') {
siblings.pop();
let before = siblings[siblings.length - 1];
before.value += prev.value + value;
prev = before;
block.ranges--;
continue;
}
push({ type: 'dot', value });
continue;
}
/**
* Text
*/
push({ type: 'text', value });
}
// Mark imbalanced braces and brackets as invalid
do {
block = stack.pop();
if (block.type !== 'root') {
block.nodes.forEach(node => {
if (!node.nodes) {
if (node.type === 'open') node.isOpen = true;
if (node.type === 'close') node.isClose = true;
if (!node.nodes) node.type = 'text';
node.invalid = true;
}
});
// get the location of the block on parent.nodes (block's siblings)
let parent = stack[stack.length - 1];
let index = parent.nodes.indexOf(block);
// replace the (invalid) block with it's nodes
parent.nodes.splice(index, 1, ...block.nodes);
}
} while (stack.length > 0);
push({ type: 'eos' });
return ast;
};
module.exports = parse;

View File

@@ -0,0 +1,32 @@
'use strict';
const utils = require('./utils');
module.exports = (ast, options = {}) => {
let stringify = (node, parent = {}) => {
let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
let 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 (let child of node.nodes) {
output += stringify(child);
}
}
return output;
};
return stringify(ast);
};

View File

@@ -0,0 +1,112 @@
'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) => {
let 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++) {
let ele = arr[i];
Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele);
}
return result;
};
flat(args);
return result;
};

View File

@@ -0,0 +1,126 @@
{
"_args": [
[
"braces@3.0.2",
"D:\\code\\vscode-code-runner"
]
],
"_from": "braces@3.0.2",
"_id": "braces@3.0.2",
"_inBundle": false,
"_integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"_location": "/braces",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "braces@3.0.2",
"name": "braces",
"escapedName": "braces",
"rawSpec": "3.0.2",
"saveSpec": null,
"fetchSpec": "3.0.2"
},
"_requiredBy": [
"/micromatch"
],
"_resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"_spec": "3.0.2",
"_where": "D:\\code\\vscode-code-runner",
"author": {
"name": "Jon Schlinkert",
"url": "https://github.com/jonschlinkert"
},
"bugs": {
"url": "https://github.com/micromatch/braces/issues"
},
"contributors": [
{
"name": "Brian Woodward",
"url": "https://twitter.com/doowb"
},
{
"name": "Elan Shanker",
"url": "https://github.com/es128"
},
{
"name": "Eugene Sharygin",
"url": "https://github.com/eush77"
},
{
"name": "hemanth.hm",
"url": "http://h3manth.com"
},
{
"name": "Jon Schlinkert",
"url": "http://twitter.com/jonschlinkert"
}
],
"dependencies": {
"fill-range": "^7.0.1"
},
"description": "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.",
"devDependencies": {
"ansi-colors": "^3.2.4",
"bash-path": "^2.0.1",
"gulp-format-md": "^2.0.0",
"mocha": "^6.1.1"
},
"engines": {
"node": ">=8"
},
"files": [
"index.js",
"lib"
],
"homepage": "https://github.com/micromatch/braces",
"keywords": [
"alpha",
"alphabetical",
"bash",
"brace",
"braces",
"expand",
"expansion",
"filepath",
"fill",
"fs",
"glob",
"globbing",
"letter",
"match",
"matches",
"matching",
"number",
"numerical",
"path",
"range",
"ranges",
"sh"
],
"license": "MIT",
"main": "index.js",
"name": "braces",
"repository": {
"type": "git",
"url": "git+https://github.com/micromatch/braces.git"
},
"scripts": {
"benchmark": "node benchmark",
"test": "mocha"
},
"verb": {
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"lint": {
"reflinks": true
},
"plugins": [
"gulp-format-md"
]
},
"version": "3.0.2"
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-present, Jon Schlinkert.
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.

View File

@@ -0,0 +1,237 @@
# fill-range [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/fill-range.svg?style=flat)](https://www.npmjs.com/package/fill-range) [![NPM monthly downloads](https://img.shields.io/npm/dm/fill-range.svg?style=flat)](https://npmjs.org/package/fill-range) [![NPM total downloads](https://img.shields.io/npm/dt/fill-range.svg?style=flat)](https://npmjs.org/package/fill-range) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/fill-range.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/fill-range)
> 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`
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save fill-range
```
## Usage
Expands numbers and letters, optionally using a `step` as the last argument. _(Numbers may be defined as JavaScript numbers or strings)_.
```js
const fill = require('fill-range');
// fill(from, to[, step, options]);
console.log(fill('1', '10')); //=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
console.log(fill('1', '10', { toRegex: true })); //=> [1-9]|10
```
**Params**
* `from`: **{String|Number}** the number or letter to start with
* `to`: **{String|Number}** the number or letter to end with
* `step`: **{String|Number|Object|Function}** Optionally pass a [step](#optionsstep) to use.
* `options`: **{Object|Function}**: See all available [options](#options)
## Examples
By default, an array of values is returned.
**Alphabetical ranges**
```js
console.log(fill('a', 'e')); //=> ['a', 'b', 'c', 'd', 'e']
console.log(fill('A', 'E')); //=> [ 'A', 'B', 'C', 'D', 'E' ]
```
**Numerical ranges**
Numbers can be defined as actual numbers or strings.
```js
console.log(fill(1, 5)); //=> [ 1, 2, 3, 4, 5 ]
console.log(fill('1', '5')); //=> [ 1, 2, 3, 4, 5 ]
```
**Negative ranges**
Numbers can be defined as actual numbers or strings.
```js
console.log(fill('-5', '-1')); //=> [ '-5', '-4', '-3', '-2', '-1' ]
console.log(fill('-5', '5')); //=> [ '-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5' ]
```
**Steps (increments)**
```js
// numerical ranges with increments
console.log(fill('0', '25', 4)); //=> [ '0', '4', '8', '12', '16', '20', '24' ]
console.log(fill('0', '25', 5)); //=> [ '0', '5', '10', '15', '20', '25' ]
console.log(fill('0', '25', 6)); //=> [ '0', '6', '12', '18', '24' ]
// alphabetical ranges with increments
console.log(fill('a', 'z', 4)); //=> [ 'a', 'e', 'i', 'm', 'q', 'u', 'y' ]
console.log(fill('a', 'z', 5)); //=> [ 'a', 'f', 'k', 'p', 'u', 'z' ]
console.log(fill('a', 'z', 6)); //=> [ 'a', 'g', 'm', 's', 'y' ]
```
## Options
### options.step
**Type**: `number` (formatted as a string or number)
**Default**: `undefined`
**Description**: The increment to use for the range. Can be used with letters or numbers.
**Example(s)**
```js
// numbers
console.log(fill('1', '10', 2)); //=> [ '1', '3', '5', '7', '9' ]
console.log(fill('1', '10', 3)); //=> [ '1', '4', '7', '10' ]
console.log(fill('1', '10', 4)); //=> [ '1', '5', '9' ]
// letters
console.log(fill('a', 'z', 5)); //=> [ 'a', 'f', 'k', 'p', 'u', 'z' ]
console.log(fill('a', 'z', 7)); //=> [ 'a', 'h', 'o', 'v' ]
console.log(fill('a', 'z', 9)); //=> [ 'a', 'j', 's' ]
```
### options.strictRanges
**Type**: `boolean`
**Default**: `false`
**Description**: By default, `null` is returned when an invalid range is passed. Enable this option to throw a `RangeError` on invalid ranges.
**Example(s)**
The following are all invalid:
```js
fill('1.1', '2'); // decimals not supported in ranges
fill('a', '2'); // incompatible range values
fill(1, 10, 'foo'); // invalid "step" argument
```
### options.stringify
**Type**: `boolean`
**Default**: `undefined`
**Description**: Cast all returned values to strings. By default, integers are returned as numbers.
**Example(s)**
```js
console.log(fill(1, 5)); //=> [ 1, 2, 3, 4, 5 ]
console.log(fill(1, 5, { stringify: true })); //=> [ '1', '2', '3', '4', '5' ]
```
### options.toRegex
**Type**: `boolean`
**Default**: `undefined`
**Description**: Create a regex-compatible source string, instead of expanding values to an array.
**Example(s)**
```js
// alphabetical range
console.log(fill('a', 'e', { toRegex: true })); //=> '[a-e]'
// alphabetical with step
console.log(fill('a', 'z', 3, { toRegex: true })); //=> 'a|d|g|j|m|p|s|v|y'
// numerical range
console.log(fill('1', '100', { toRegex: true })); //=> '[1-9]|[1-9][0-9]|100'
// numerical range with zero padding
console.log(fill('000001', '100000', { toRegex: true }));
//=> '0{5}[1-9]|0{4}[1-9][0-9]|0{3}[1-9][0-9]{2}|0{2}[1-9][0-9]{3}|0[1-9][0-9]{4}|100000'
```
### options.transform
**Type**: `function`
**Default**: `undefined`
**Description**: Customize each value in the returned array (or [string](#optionstoRegex)). _(you can also pass this function as the last argument to `fill()`)_.
**Example(s)**
```js
// add zero padding
console.log(fill(1, 5, value => String(value).padStart(4, '0')));
//=> ['0001', '0002', '0003', '0004', '0005']
```
## 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).
</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>
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 116 | [jonschlinkert](https://github.com/jonschlinkert) |
| 4 | [paulmillr](https://github.com/paulmillr) |
| 2 | [realityking](https://github.com/realityking) |
| 2 | [bluelovers](https://github.com/bluelovers) |
| 1 | [edorivai](https://github.com/edorivai) |
| 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
### Author
**Jon Schlinkert**
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
Please consider supporting me on Patreon, or [start your own Patreon page](https://patreon.com/invite/bxpbvm)!
<a href="https://www.patreon.com/jonschlinkert">
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" height="50">
</a>
### License
Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 08, 2019._

View File

@@ -0,0 +1,249 @@
/*!
* fill-range <https://github.com/jonschlinkert/fill-range>
*
* Copyright (c) 2014-present, Jon Schlinkert.
* Licensed under the MIT License.
*/
'use strict';
const util = require('util');
const toRegexRange = require('to-regex-range');
const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
const transform = toNumber => {
return value => toNumber === true ? Number(value) : String(value);
};
const isValidValue = value => {
return typeof value === 'number' || (typeof value === 'string' && value !== '');
};
const isNumber = num => Number.isInteger(+num);
const zeros = input => {
let value = `${input}`;
let index = -1;
if (value[0] === '-') value = value.slice(1);
if (value === '0') return false;
while (value[++index] === '0');
return index > 0;
};
const stringify = (start, end, options) => {
if (typeof start === 'string' || typeof end === 'string') {
return true;
}
return options.stringify === true;
};
const pad = (input, maxLength, toNumber) => {
if (maxLength > 0) {
let dash = input[0] === '-' ? '-' : '';
if (dash) input = input.slice(1);
input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0'));
}
if (toNumber === false) {
return String(input);
}
return input;
};
const toMaxLen = (input, maxLength) => {
let negative = input[0] === '-' ? '-' : '';
if (negative) {
input = input.slice(1);
maxLength--;
}
while (input.length < maxLength) input = '0' + input;
return negative ? ('-' + input) : input;
};
const toSequence = (parts, options) => {
parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
let prefix = options.capture ? '' : '?:';
let positives = '';
let negatives = '';
let result;
if (parts.positives.length) {
positives = parts.positives.join('|');
}
if (parts.negatives.length) {
negatives = `-(${prefix}${parts.negatives.join('|')})`;
}
if (positives && negatives) {
result = `${positives}|${negatives}`;
} else {
result = positives || negatives;
}
if (options.wrap) {
return `(${prefix}${result})`;
}
return result;
};
const toRange = (a, b, isNumbers, options) => {
if (isNumbers) {
return toRegexRange(a, b, { wrap: false, ...options });
}
let start = String.fromCharCode(a);
if (a === b) return start;
let stop = String.fromCharCode(b);
return `[${start}-${stop}]`;
};
const toRegex = (start, end, options) => {
if (Array.isArray(start)) {
let wrap = options.wrap === true;
let prefix = options.capture ? '' : '?:';
return wrap ? `(${prefix}${start.join('|')})` : start.join('|');
}
return toRegexRange(start, end, options);
};
const rangeError = (...args) => {
return new RangeError('Invalid range arguments: ' + util.inspect(...args));
};
const invalidRange = (start, end, options) => {
if (options.strictRanges === true) throw rangeError([start, end]);
return [];
};
const invalidStep = (step, options) => {
if (options.strictRanges === true) {
throw new TypeError(`Expected step "${step}" to be a number`);
}
return [];
};
const fillNumbers = (start, end, step = 1, options = {}) => {
let a = Number(start);
let b = Number(end);
if (!Number.isInteger(a) || !Number.isInteger(b)) {
if (options.strictRanges === true) throw rangeError([start, end]);
return [];
}
// fix negative zero
if (a === 0) a = 0;
if (b === 0) b = 0;
let descending = a > b;
let startString = String(start);
let endString = String(end);
let stepString = String(step);
step = Math.max(Math.abs(step), 1);
let padded = zeros(startString) || zeros(endString) || zeros(stepString);
let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0;
let toNumber = padded === false && stringify(start, end, options) === false;
let format = options.transform || transform(toNumber);
if (options.toRegex && step === 1) {
return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options);
}
let parts = { negatives: [], positives: [] };
let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num));
let range = [];
let index = 0;
while (descending ? a >= b : a <= b) {
if (options.toRegex === true && step > 1) {
push(a);
} else {
range.push(pad(format(a, index), maxLen, toNumber));
}
a = descending ? a - step : a + step;
index++;
}
if (options.toRegex === true) {
return step > 1
? toSequence(parts, options)
: toRegex(range, null, { wrap: false, ...options });
}
return range;
};
const fillLetters = (start, end, step = 1, options = {}) => {
if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) {
return invalidRange(start, end, options);
}
let format = options.transform || (val => String.fromCharCode(val));
let a = `${start}`.charCodeAt(0);
let b = `${end}`.charCodeAt(0);
let descending = a > b;
let min = Math.min(a, b);
let max = Math.max(a, b);
if (options.toRegex && step === 1) {
return toRange(min, max, false, options);
}
let range = [];
let index = 0;
while (descending ? a >= b : a <= b) {
range.push(format(a, index));
a = descending ? a - step : a + step;
index++;
}
if (options.toRegex === true) {
return toRegex(range, null, { wrap: false, options });
}
return range;
};
const fill = (start, end, step, options = {}) => {
if (end == null && isValidValue(start)) {
return [start];
}
if (!isValidValue(start) || !isValidValue(end)) {
return invalidRange(start, end, options);
}
if (typeof step === 'function') {
return fill(start, end, 1, { transform: step });
}
if (isObject(step)) {
return fill(start, end, 0, step);
}
let opts = { ...options };
if (opts.capture === true) opts.wrap = true;
step = step || opts.step || 1;
if (!isNumber(step)) {
if (step != null && !isObject(step)) return invalidStep(step, opts);
return fill(start, end, 1, step);
}
if (isNumber(start) && isNumber(end)) {
return fillNumbers(start, end, step, opts);
}
return fillLetters(start, end, Math.max(Math.abs(step), 1), opts);
};
module.exports = fill;

View File

@@ -0,0 +1,117 @@
{
"_args": [
[
"fill-range@7.0.1",
"D:\\code\\vscode-code-runner"
]
],
"_from": "fill-range@7.0.1",
"_id": "fill-range@7.0.1",
"_inBundle": false,
"_integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"_location": "/fill-range",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "fill-range@7.0.1",
"name": "fill-range",
"escapedName": "fill-range",
"rawSpec": "7.0.1",
"saveSpec": null,
"fetchSpec": "7.0.1"
},
"_requiredBy": [
"/braces"
],
"_resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"_spec": "7.0.1",
"_where": "D:\\code\\vscode-code-runner",
"author": {
"name": "Jon Schlinkert",
"url": "https://github.com/jonschlinkert"
},
"bugs": {
"url": "https://github.com/jonschlinkert/fill-range/issues"
},
"contributors": [
{
"name": "Edo Rivai",
"url": "edo.rivai.nl"
},
{
"name": "Jon Schlinkert",
"url": "http://twitter.com/jonschlinkert"
},
{
"name": "Paul Miller",
"url": "paulmillr.com"
},
{
"name": "Rouven Weßling",
"url": "www.rouvenwessling.de"
},
{
"url": "https://github.com/wtgtybhertgeghgtwtg"
}
],
"dependencies": {
"to-regex-range": "^5.0.1"
},
"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`",
"devDependencies": {
"gulp-format-md": "^2.0.0",
"mocha": "^6.1.1"
},
"engines": {
"node": ">=8"
},
"files": [
"index.js"
],
"homepage": "https://github.com/jonschlinkert/fill-range",
"keywords": [
"alpha",
"alphabetical",
"array",
"bash",
"brace",
"expand",
"expansion",
"fill",
"glob",
"match",
"matches",
"matching",
"number",
"numerical",
"range",
"ranges",
"regex",
"sh"
],
"license": "MIT",
"main": "index.js",
"name": "fill-range",
"repository": {
"type": "git",
"url": "git+https://github.com/jonschlinkert/fill-range.git"
},
"scripts": {
"test": "mocha"
},
"verb": {
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
}
},
"version": "7.0.1"
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-present, Jon Schlinkert.
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.

View File

@@ -0,0 +1,187 @@
# is-number [![NPM version](https://img.shields.io/npm/v/is-number.svg?style=flat)](https://www.npmjs.com/package/is-number) [![NPM monthly downloads](https://img.shields.io/npm/dm/is-number.svg?style=flat)](https://npmjs.org/package/is-number) [![NPM total downloads](https://img.shields.io/npm/dt/is-number.svg?style=flat)](https://npmjs.org/package/is-number) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/is-number.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/is-number)
> Returns true if the value is a finite number.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save is-number
```
## Why is this needed?
In JavaScript, it's not always as straightforward as it should be to reliably check if a value is a number. It's common for devs to use `+`, `-`, or `Number()` to cast a string value to a number (for example, when values are returned from user input, regex matches, parsers, etc). But there are many non-intuitive edge cases that yield unexpected results:
```js
console.log(+[]); //=> 0
console.log(+''); //=> 0
console.log(+' '); //=> 0
console.log(typeof NaN); //=> 'number'
```
This library offers a performant way to smooth out edge cases like these.
## Usage
```js
const isNumber = require('is-number');
```
See the [tests](./test.js) for more examples.
### true
```js
isNumber(5e3); // true
isNumber(0xff); // true
isNumber(-1.1); // true
isNumber(0); // true
isNumber(1); // true
isNumber(1.1); // true
isNumber(10); // true
isNumber(10.10); // true
isNumber(100); // true
isNumber('-1.1'); // true
isNumber('0'); // true
isNumber('012'); // true
isNumber('0xff'); // true
isNumber('1'); // true
isNumber('1.1'); // true
isNumber('10'); // true
isNumber('10.10'); // true
isNumber('100'); // true
isNumber('5e3'); // true
isNumber(parseInt('012')); // true
isNumber(parseFloat('012')); // true
```
### False
Everything else is false, as you would expect:
```js
isNumber(Infinity); // false
isNumber(NaN); // false
isNumber(null); // false
isNumber(undefined); // false
isNumber(''); // false
isNumber(' '); // false
isNumber('foo'); // false
isNumber([1]); // false
isNumber([]); // false
isNumber(function () {}); // false
isNumber({}); // false
```
## Release history
### 7.0.0
* Refactor. Now uses `.isFinite` if it exists.
* Performance is about the same as v6.0 when the value is a string or number. But it's now 3x-4x faster when the value is not a string or number.
### 6.0.0
* Optimizations, thanks to @benaadams.
### 5.0.0
**Breaking changes**
* removed support for `instanceof Number` and `instanceof String`
## Benchmarks
As with all benchmarks, take these with a grain of salt. See the [benchmarks](./benchmark/index.js) for more detail.
```
# all
v7.0 x 413,222 ops/sec ±2.02% (86 runs sampled)
v6.0 x 111,061 ops/sec ±1.29% (85 runs sampled)
parseFloat x 317,596 ops/sec ±1.36% (86 runs sampled)
fastest is 'v7.0'
# string
v7.0 x 3,054,496 ops/sec ±1.05% (89 runs sampled)
v6.0 x 2,957,781 ops/sec ±0.98% (88 runs sampled)
parseFloat x 3,071,060 ops/sec ±1.13% (88 runs sampled)
fastest is 'parseFloat,v7.0'
# number
v7.0 x 3,146,895 ops/sec ±0.89% (89 runs sampled)
v6.0 x 3,214,038 ops/sec ±1.07% (89 runs sampled)
parseFloat x 3,077,588 ops/sec ±1.07% (87 runs sampled)
fastest is 'v6.0'
```
## 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).
</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>
### Related projects
You might also be interested in these projects:
* [is-plain-object](https://www.npmjs.com/package/is-plain-object): Returns true if an object was created by the `Object` constructor. | [homepage](https://github.com/jonschlinkert/is-plain-object "Returns true if an object was created by the `Object` constructor.")
* [is-primitive](https://www.npmjs.com/package/is-primitive): Returns `true` if the value is a primitive. | [homepage](https://github.com/jonschlinkert/is-primitive "Returns `true` if the value is a primitive. ")
* [isobject](https://www.npmjs.com/package/isobject): Returns true if the value is an object and not an array or null. | [homepage](https://github.com/jonschlinkert/isobject "Returns true if the value is an object and not an array or null.")
* [kind-of](https://www.npmjs.com/package/kind-of): Get the native type of a value. | [homepage](https://github.com/jonschlinkert/kind-of "Get the native type of a value.")
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 49 | [jonschlinkert](https://github.com/jonschlinkert) |
| 5 | [charlike-old](https://github.com/charlike-old) |
| 1 | [benaadams](https://github.com/benaadams) |
| 1 | [realityking](https://github.com/realityking) |
### Author
**Jon Schlinkert**
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
### License
Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 15, 2018._

View File

@@ -0,0 +1,18 @@
/*!
* is-number <https://github.com/jonschlinkert/is-number>
*
* Copyright (c) 2014-present, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
module.exports = function(num) {
if (typeof num === 'number') {
return num - num === 0;
}
if (typeof num === 'string' && num.trim() !== '') {
return Number.isFinite ? Number.isFinite(+num) : isFinite(+num);
}
return false;
};

View File

@@ -0,0 +1,125 @@
{
"_args": [
[
"is-number@7.0.0",
"D:\\code\\vscode-code-runner"
]
],
"_from": "is-number@7.0.0",
"_id": "is-number@7.0.0",
"_inBundle": false,
"_integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"_location": "/is-number",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "is-number@7.0.0",
"name": "is-number",
"escapedName": "is-number",
"rawSpec": "7.0.0",
"saveSpec": null,
"fetchSpec": "7.0.0"
},
"_requiredBy": [
"/to-regex-range"
],
"_resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"_spec": "7.0.0",
"_where": "D:\\code\\vscode-code-runner",
"author": {
"name": "Jon Schlinkert",
"url": "https://github.com/jonschlinkert"
},
"bugs": {
"url": "https://github.com/jonschlinkert/is-number/issues"
},
"contributors": [
{
"name": "Jon Schlinkert",
"url": "http://twitter.com/jonschlinkert"
},
{
"name": "Olsten Larck",
"url": "https://i.am.charlike.online"
},
{
"name": "Rouven Weßling",
"url": "www.rouvenwessling.de"
}
],
"description": "Returns true if a number or string value is a finite number. Useful for regex matches, parsing, user input, etc.",
"devDependencies": {
"ansi": "^0.3.1",
"benchmark": "^2.1.4",
"gulp-format-md": "^1.0.0",
"mocha": "^3.5.3"
},
"engines": {
"node": ">=0.12.0"
},
"files": [
"index.js"
],
"homepage": "https://github.com/jonschlinkert/is-number",
"keywords": [
"cast",
"check",
"coerce",
"coercion",
"finite",
"integer",
"is",
"isnan",
"is-nan",
"is-num",
"is-number",
"isnumber",
"isfinite",
"istype",
"kind",
"math",
"nan",
"num",
"number",
"numeric",
"parseFloat",
"parseInt",
"test",
"type",
"typeof",
"value"
],
"license": "MIT",
"main": "index.js",
"name": "is-number",
"repository": {
"type": "git",
"url": "git+https://github.com/jonschlinkert/is-number.git"
},
"scripts": {
"test": "mocha"
},
"verb": {
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"related": {
"list": [
"is-plain-object",
"is-primitive",
"isobject",
"kind-of"
]
},
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
}
},
"version": "7.0.0"
}

View File

@@ -0,0 +1,108 @@
# Release history
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
<details>
<summary><strong>Guiding Principles</strong></summary>
- Changelogs are for humans, not machines.
- There should be an entry for every single version.
- The same types of changes should be grouped.
- Versions and sections should be linkable.
- The latest version comes first.
- The release date of each versions is displayed.
- Mention whether you follow Semantic Versioning.
</details>
<details>
<summary><strong>Types of changes</strong></summary>
Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_):
- `Added` for new features.
- `Changed` for changes in existing functionality.
- `Deprecated` for soon-to-be removed features.
- `Removed` for now removed features.
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.
</details>
## [4.0.0] - 2019-03-20
### Added
- Adds support for `options.onMatch`. See the readme for details
- Adds support for `options.onIgnore`. See the readme for details
- Adds support for `options.onResult`. See the readme for details
### Breaking changes
- Removed support for passing an array of brace patterns to `micromatch.braces()`.
- To strictly enforce closing brackets (for `{`, `[`, and `(`), you must now use `strictBrackets=true` instead of `strictErrors`.
- `cache` - caching and all related options and methods have been removed
- `options.unixify` was renamed to `options.windows`
- `options.nodupes` Was removed. Duplicates are always removed by default. You can override this with custom behavior by using the `onMatch`, `onResult` and `onIgnore` functions.
- `options.snapdragon` was removed, as snapdragon is no longer used.
- `options.sourcemap` was removed, as snapdragon is no longer used, which provided sourcemap support.
## [3.0.0] - 2017-04-11
Complete overhaul, with 36,000+ new unit tests validated against actual output generated by Bash and minimatch. More specifically, 35,000+ of the tests:
- micromatch results are directly compared to bash results
- in rare cases, when micromatch and bash disagree, micromatch's results are compared to minimatch's results
- micromatch is much more accurate than minimatch, so there were cases where I had to make assumptions. I'll try to document these.
This refactor introduces a parser and compiler that are supersets of more granular parsers and compilers from other sub-modules. Each of these sub-modules has a singular responsibility and focuses on a certain type of matching that aligns with a specific part of the Bash "expansion" API.
These sub-modules work like plugins to seamlessly create the micromatch parser/compiler, so that strings are parsed in one pass, an [AST is created](https://gist.github.com/jonschlinkert/099c8914f56529f75bc757cc9e5e8e2a), then a new string is generated by the compiler.
Here are those sub-modules with links to related prs on those modules if you want to see how they contribute to this code:
[nanomatch](https://github.com/jonschlinkert/nanomatch) (new library) - glob expansion (`*`, `**`, `?` and `[...]`))
[braces](https://github.com/jonschlinkert/braces/pull/10) - brace expansion (`{1..10}`, `{a,b,c}`, etc)
[extglob](https://github.com/jonschlinkert/extglob/pull/5) - extended globs (`!(a|b)`, `@(!(foo|bar))`, etc)
[expand-brackets](https://github.com/jonschlinkert/expand-brackets/pull/5) - POSIX character classes `[[:alpha:][:digit:]]`
**Added**
- source map support (optionally created when using parse or compile - I have no idea what the use case is yet, but they come for free) (note that source maps are not generated for brace expansion at present, since the braces compiler uses a different strategy. I'll update if/when this changes).
- parser is exposed, so that implementors can customize or override specific micromatch parsers if necessary
- compiler is exposed, so that implementors can customize or override specific micromatch compilers if necessary
**Fixed**
- more accurate matching (passes 100% of Bash 4.3 of the brace expansion and extglob unit tests, as well as all Bash glob tests that are relevant to node.js usage, all minimatch tests, all brace-expansion tests, and also passes a couple of tests that bash fails)
- even safer - micromatch has always generated optimized patterns so it's not subject to DoS exploits like minimatch (completely different than the regex DoS issue, minimatch and multimatch are still openly exposed to being used for DoS attacks), but more safeguards were built into this refactor
**Changed**
- the public API of this library did not change in this version and should be safe to upgrade without changing implentor code. However, we have released this as a major version for the following reasons:
- out of an abundance of caution due to the large amount of code changed in this release
- we have improved parser accuracy to such a degree that some implementors using invalid globs have noted change in behavior. If this is the case for you, please check that you are using a valid glob expression before logging a bug with this library
## [1.0.1] - 2016-12-12
**Added**
- Support for windows path edge cases where backslashes are used in brackets or other unusual combinations.
## [1.0.0] - 2016-12-12
Stable release.
## [0.1.0] - 2016-10-08
First release.
[Unreleased]: https://github.com/jonschlinkert/micromatch/compare/0.1.0...HEAD
[0.2.0]: https://github.com/jonschlinkert/micromatch/compare/0.1.0...0.2.0
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-present, Jon Schlinkert.
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 it is too large Load Diff

View File

@@ -0,0 +1,467 @@
'use strict';
const util = require('util');
const braces = require('braces');
const picomatch = require('picomatch');
const utils = require('picomatch/lib/utils');
const isEmptyString = val => typeof val === 'string' && (val === '' || val === './');
/**
* Returns an array of strings that match one or more glob patterns.
*
* ```js
* const mm = require('micromatch');
* // mm(list, patterns[, options]);
*
* console.log(mm(['a.js', 'a.txt'], ['*.js']));
* //=> [ 'a.js' ]
* ```
* @param {String|Array<string>} list List of strings to match.
* @param {String|Array<string>} patterns One or more glob patterns to use for matching.
* @param {Object} options See available [options](#options)
* @return {Array} Returns an array of matches
* @summary false
* @api public
*/
const micromatch = (list, patterns, options) => {
patterns = [].concat(patterns);
list = [].concat(list);
let omit = new Set();
let keep = new Set();
let items = new Set();
let negatives = 0;
let onResult = state => {
items.add(state.output);
if (options && options.onResult) {
options.onResult(state);
}
};
for (let i = 0; i < patterns.length; i++) {
let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true);
let negated = isMatch.state.negated || isMatch.state.negatedExtglob;
if (negated) negatives++;
for (let item of list) {
let matched = isMatch(item, true);
let match = negated ? !matched.isMatch : matched.isMatch;
if (!match) continue;
if (negated) {
omit.add(matched.output);
} else {
omit.delete(matched.output);
keep.add(matched.output);
}
}
}
let result = negatives === patterns.length ? [...items] : [...keep];
let matches = result.filter(item => !omit.has(item));
if (options && matches.length === 0) {
if (options.failglob === true) {
throw new Error(`No matches found for "${patterns.join(', ')}"`);
}
if (options.nonull === true || options.nullglob === true) {
return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns;
}
}
return matches;
};
/**
* Backwards compatibility
*/
micromatch.match = micromatch;
/**
* Returns a matcher function from the given glob `pattern` and `options`.
* The returned function takes a string to match as its only argument and returns
* true if the string is a match.
*
* ```js
* const mm = require('micromatch');
* // mm.matcher(pattern[, options]);
*
* const isMatch = mm.matcher('*.!(*a)');
* console.log(isMatch('a.a')); //=> false
* console.log(isMatch('a.b')); //=> true
* ```
* @param {String} `pattern` Glob pattern
* @param {Object} `options`
* @return {Function} Returns a matcher function.
* @api public
*/
micromatch.matcher = (pattern, options) => picomatch(pattern, options);
/**
* Returns true if **any** of the given glob `patterns` match the specified `string`.
*
* ```js
* const mm = require('micromatch');
* // mm.isMatch(string, patterns[, options]);
*
* console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true
* console.log(mm.isMatch('a.a', 'b.*')); //=> false
* ```
* @param {String} str The string to test.
* @param {String|Array} patterns One or more glob patterns to use for matching.
* @param {Object} [options] See available [options](#options).
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
/**
* Backwards compatibility
*/
micromatch.any = micromatch.isMatch;
/**
* Returns a list of strings that _**do not match any**_ of the given `patterns`.
*
* ```js
* const mm = require('micromatch');
* // mm.not(list, patterns[, options]);
*
* console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a'));
* //=> ['b.b', 'c.c']
* ```
* @param {Array} `list` Array of strings to match.
* @param {String|Array} `patterns` One or more glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Array} Returns an array of strings that **do not match** the given patterns.
* @api public
*/
micromatch.not = (list, patterns, options = {}) => {
patterns = [].concat(patterns).map(String);
let result = new Set();
let items = [];
let onResult = state => {
if (options.onResult) options.onResult(state);
items.push(state.output);
};
let matches = micromatch(list, patterns, { ...options, onResult });
for (let item of items) {
if (!matches.includes(item)) {
result.add(item);
}
}
return [...result];
};
/**
* Returns true if the given `string` contains the given pattern. Similar
* to [.isMatch](#isMatch) but the pattern can match any part of the string.
*
* ```js
* var mm = require('micromatch');
* // mm.contains(string, pattern[, options]);
*
* console.log(mm.contains('aa/bb/cc', '*b'));
* //=> true
* console.log(mm.contains('aa/bb/cc', '*d'));
* //=> false
* ```
* @param {String} `str` The string to match.
* @param {String|Array} `patterns` Glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if the patter matches any part of `str`.
* @api public
*/
micromatch.contains = (str, pattern, options) => {
if (typeof str !== 'string') {
throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
}
if (Array.isArray(pattern)) {
return pattern.some(p => micromatch.contains(str, p, options));
}
if (typeof pattern === 'string') {
if (isEmptyString(str) || isEmptyString(pattern)) {
return false;
}
if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) {
return true;
}
}
return micromatch.isMatch(str, pattern, { ...options, contains: true });
};
/**
* Filter the keys of the given object with the given `glob` pattern
* and `options`. Does not attempt to match nested keys. If you need this feature,
* use [glob-object][] instead.
*
* ```js
* const mm = require('micromatch');
* // mm.matchKeys(object, patterns[, options]);
*
* const obj = { aa: 'a', ab: 'b', ac: 'c' };
* console.log(mm.matchKeys(obj, '*b'));
* //=> { ab: 'b' }
* ```
* @param {Object} `object` The object with keys to filter.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Object} Returns an object with only keys that match the given patterns.
* @api public
*/
micromatch.matchKeys = (obj, patterns, options) => {
if (!utils.isObject(obj)) {
throw new TypeError('Expected the first argument to be an object');
}
let keys = micromatch(Object.keys(obj), patterns, options);
let res = {};
for (let key of keys) res[key] = obj[key];
return res;
};
/**
* Returns true if some of the strings in the given `list` match any of the given glob `patterns`.
*
* ```js
* const mm = require('micromatch');
* // mm.some(list, patterns[, options]);
*
* console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
* // true
* console.log(mm.some(['foo.js'], ['*.js', '!foo.js']));
* // false
* ```
* @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.some = (list, patterns, options) => {
let items = [].concat(list);
for (let pattern of [].concat(patterns)) {
let isMatch = picomatch(String(pattern), options);
if (items.some(item => isMatch(item))) {
return true;
}
}
return false;
};
/**
* Returns true if every string in the given `list` matches
* any of the given glob `patterns`.
*
* ```js
* const mm = require('micromatch');
* // mm.every(list, patterns[, options]);
*
* console.log(mm.every('foo.js', ['foo.js']));
* // true
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js']));
* // true
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
* // false
* console.log(mm.every(['foo.js'], ['*.js', '!foo.js']));
* // false
* ```
* @param {String|Array} `list` The string or array of strings to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.every = (list, patterns, options) => {
let items = [].concat(list);
for (let pattern of [].concat(patterns)) {
let isMatch = picomatch(String(pattern), options);
if (!items.every(item => isMatch(item))) {
return false;
}
}
return true;
};
/**
* Returns true if **all** of the given `patterns` match
* the specified string.
*
* ```js
* const mm = require('micromatch');
* // mm.all(string, patterns[, options]);
*
* console.log(mm.all('foo.js', ['foo.js']));
* // true
*
* console.log(mm.all('foo.js', ['*.js', '!foo.js']));
* // false
*
* console.log(mm.all('foo.js', ['*.js', 'foo.js']));
* // true
*
* console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js']));
* // true
* ```
* @param {String|Array} `str` The string to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.all = (str, patterns, options) => {
if (typeof str !== 'string') {
throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
}
return [].concat(patterns).every(p => picomatch(p, options)(str));
};
/**
* Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match.
*
* ```js
* const mm = require('micromatch');
* // mm.capture(pattern, string[, options]);
*
* console.log(mm.capture('test/*.js', 'test/foo.js'));
* //=> ['foo']
* console.log(mm.capture('test/*.js', 'foo/bar.css'));
* //=> null
* ```
* @param {String} `glob` Glob pattern to use for matching.
* @param {String} `input` String to match
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns an array of captures if the input matches the glob pattern, otherwise `null`.
* @api public
*/
micromatch.capture = (glob, input, options) => {
let posix = utils.isWindows(options);
let regex = picomatch.makeRe(String(glob), { ...options, capture: true });
let match = regex.exec(posix ? utils.toPosixSlashes(input) : input);
if (match) {
return match.slice(1).map(v => v === void 0 ? '' : v);
}
};
/**
* Create a regular expression from the given glob `pattern`.
*
* ```js
* const mm = require('micromatch');
* // mm.makeRe(pattern[, options]);
*
* console.log(mm.makeRe('*.js'));
* //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
* ```
* @param {String} `pattern` A glob pattern to convert to regex.
* @param {Object} `options`
* @return {RegExp} Returns a regex created from the given pattern.
* @api public
*/
micromatch.makeRe = (...args) => picomatch.makeRe(...args);
/**
* Scan a glob pattern to separate the pattern into segments. Used
* by the [split](#split) method.
*
* ```js
* const mm = require('micromatch');
* const state = mm.scan(pattern[, options]);
* ```
* @param {String} `pattern`
* @param {Object} `options`
* @return {Object} Returns an object with
* @api public
*/
micromatch.scan = (...args) => picomatch.scan(...args);
/**
* Parse a glob pattern to create the source string for a regular
* expression.
*
* ```js
* const mm = require('micromatch');
* const state = mm(pattern[, options]);
* ```
* @param {String} `glob`
* @param {Object} `options`
* @return {Object} Returns an object with useful properties and output to be used as regex source string.
* @api public
*/
micromatch.parse = (patterns, options) => {
let res = [];
for (let pattern of [].concat(patterns || [])) {
for (let str of braces(String(pattern), options)) {
res.push(picomatch.parse(str, options));
}
}
return res;
};
/**
* Process the given brace `pattern`.
*
* ```js
* const { braces } = require('micromatch');
* console.log(braces('foo/{a,b,c}/bar'));
* //=> [ 'foo/(a|b|c)/bar' ]
*
* console.log(braces('foo/{a,b,c}/bar', { expand: true }));
* //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ]
* ```
* @param {String} `pattern` String with brace pattern to process.
* @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options.
* @return {Array}
* @api public
*/
micromatch.braces = (pattern, options) => {
if (typeof pattern !== 'string') throw new TypeError('Expected a string');
if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) {
return [pattern];
}
return braces(pattern, options);
};
/**
* Expand braces
*/
micromatch.braceExpand = (pattern, options) => {
if (typeof pattern !== 'string') throw new TypeError('Expected a string');
return micromatch.braces(pattern, { ...options, expand: true });
};
/**
* Expose micromatch
*/
module.exports = micromatch;

View File

@@ -0,0 +1,194 @@
{
"_args": [
[
"micromatch@4.0.2",
"D:\\code\\vscode-code-runner"
]
],
"_from": "micromatch@4.0.2",
"_id": "micromatch@4.0.2",
"_inBundle": false,
"_integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"_location": "/micromatch",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "micromatch@4.0.2",
"name": "micromatch",
"escapedName": "micromatch",
"rawSpec": "4.0.2",
"saveSpec": null,
"fetchSpec": "4.0.2"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"_spec": "4.0.2",
"_where": "D:\\code\\vscode-code-runner",
"author": {
"name": "Jon Schlinkert",
"url": "https://github.com/jonschlinkert"
},
"bugs": {
"url": "https://github.com/micromatch/micromatch/issues"
},
"contributors": [
{
"url": "https://github.com/DianeLooney"
},
{
"name": "Amila Welihinda",
"url": "amilajack.com"
},
{
"name": "Bogdan Chadkin",
"url": "https://github.com/TrySound"
},
{
"name": "Brian Woodward",
"url": "https://twitter.com/doowb"
},
{
"name": "Devon Govett",
"url": "http://badassjs.com"
},
{
"name": "Elan Shanker",
"url": "https://github.com/es128"
},
{
"name": "Fabrício Matté",
"url": "https://ultcombo.js.org"
},
{
"name": "Jon Schlinkert",
"url": "http://twitter.com/jonschlinkert"
},
{
"name": "Martin Kolárik",
"url": "https://kolarik.sk"
},
{
"name": "Olsten Larck",
"url": "https://i.am.charlike.online"
},
{
"name": "Paul Miller",
"url": "paulmillr.com"
},
{
"name": "Tom Byrer",
"url": "https://github.com/tomByrer"
},
{
"name": "Tyler Akins",
"url": "http://rumkin.com"
},
{
"name": "Peter Bright",
"email": "drpizza@quiscalusmexicanus.org",
"url": "https://github.com/drpizza"
}
],
"dependencies": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
},
"description": "Glob matching for javascript/node.js. A replacement and faster alternative to minimatch and multimatch.",
"devDependencies": {
"fill-range": "^7.0.1",
"gulp-format-md": "^2.0.0",
"minimatch": "^3.0.4",
"mocha": "^5.2.0",
"time-require": "github:jonschlinkert/time-require"
},
"engines": {
"node": ">=8"
},
"files": [
"index.js"
],
"homepage": "https://github.com/micromatch/micromatch",
"keywords": [
"bash",
"bracket",
"character-class",
"expand",
"expansion",
"expression",
"extglob",
"extglobs",
"file",
"files",
"filter",
"find",
"glob",
"globbing",
"globs",
"globstar",
"lookahead",
"lookaround",
"lookbehind",
"match",
"matcher",
"matches",
"matching",
"micromatch",
"minimatch",
"multimatch",
"negate",
"negation",
"path",
"pattern",
"patterns",
"posix",
"regex",
"regexp",
"regular",
"shell",
"star",
"wildcard"
],
"license": "MIT",
"main": "index.js",
"name": "micromatch",
"repository": {
"type": "git",
"url": "git+https://github.com/micromatch/micromatch.git"
},
"scripts": {
"test": "mocha"
},
"verb": {
"toc": "collapsible",
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"related": {
"list": [
"braces",
"expand-brackets",
"extglob",
"fill-range",
"nanomatch"
]
},
"reflinks": [
"extglob",
"fill-range",
"glob-object",
"minimatch",
"multimatch"
]
},
"version": "4.0.2"
}

View File

@@ -0,0 +1,69 @@
# Release history
**All notable changes to this project will be documented in this file.**
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
<details>
<summary><strong>Guiding Principles</strong></summary>
- Changelogs are for humans, not machines.
- There should be an entry for every single version.
- The same types of changes should be grouped.
- Versions and sections should be linkable.
- The latest version comes first.
- The release date of each versions is displayed.
- Mention whether you follow Semantic Versioning.
</details>
<details>
<summary><strong>Types of changes</strong></summary>
Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_):
- `Added` for new features.
- `Changed` for changes in existing functionality.
- `Deprecated` for soon-to-be removed features.
- `Removed` for now removed features.
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.
</details>
## [2.0.4] - 2019-04-10
### Fixed
- Readme link [fixed](https://github.com/micromatch/picomatch/pull/13/commits/a96ab3aa2b11b6861c23289964613d85563b05df) by @danez.
- `options.capture` now works as expected when fastpaths are enabled. See https://github.com/micromatch/picomatch/pull/12/commits/26aefd71f1cfaf95c37f1c1fcab68a693b037304. Thanks to @DrPizza.
## [2.0.0] - 2019-04-10
### Added
- Adds support for `options.onIgnore`. See the readme for details
- Adds support for `options.onResult`. See the readme for details
### Breaking changes
- The unixify option was renamed to `windows`
- caching and all related options and methods have been removed
## [1.0.0] - 2018-11-05
- adds `.onMatch` option
- improvements to `.scan` method
- numerous improvements and optimizations for matching and parsing
- better windows path handling
## 0.1.0 - 2017-04-13
First release.
[2.0.4]: https://github.com/jonschlinkert/micromatch/compare/2.0.0...2.0.4
[2.0.0]: https://github.com/jonschlinkert/micromatch/compare/1.0.0...2.0.0
[1.0.0]: https://github.com/jonschlinkert/micromatch/compare/0.1.0...1.0.0
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017-present, Jon Schlinkert.
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.

View File

@@ -0,0 +1,605 @@
<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://travis-ci.org/micromatch/picomatch">
<img src="https://img.shields.io/travis/micromatch/picomatch.svg" alt="travis" />
</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>
## 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#L30)
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#L109)
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#L153)
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#L175)
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#L191)
Parse a glob pattern to create the source string for a regular expression.
**Params**
* `glob` **{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(glob[, options]);
```
### [.scan](lib/picomatch.js#L215)
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',
// base: 'foo',
// glob: '*.js',
// negated: true,
// isGlob: true }
```
### [.makeRe](lib/picomatch.js#L233)
Create a regular expression from a glob pattern.
**Params**
* `input` **{String}**: A glob pattern to convert to regex.
* `options` **{Object}**
* `returns` **{RegExp}**: Returns a regex created from the given pattern.
**Example**
```js
const picomatch = require('picomatch');
// picomatch.makeRe(input[, options]);
console.log(picomatch.makeRe('*.js'));
//=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
```
### [.toRegex](lib/picomatch.js#L294)
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
| **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` | `boolean` | `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. |
| `lookbehinds` | `boolean` | `true` | Support regex positive and negative lookbehinds. Note that you must be using Node 8.1.10 or higher to enable regex lookbehinds. |
| `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 POSX 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 compatitibility. |
<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
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // false
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // false
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // true
// 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 libary 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).

View File

@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/picomatch');

View File

@@ -0,0 +1,179 @@
'use strict';
const path = require('path');
const WIN_SLASH = '\\\\/';
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
/**
* Posix glob regex
*/
const DOT_LITERAL = '\\.';
const PLUS_LITERAL = '\\+';
const QMARK_LITERAL = '\\?';
const SLASH_LITERAL = '\\/';
const ONE_CHAR = '(?=.)';
const QMARK = '[^/]';
const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
const NO_DOT = `(?!${DOT_LITERAL})`;
const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`;
const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`;
const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`;
const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`;
const STAR = `${QMARK}*?`;
const POSIX_CHARS = {
DOT_LITERAL,
PLUS_LITERAL,
QMARK_LITERAL,
SLASH_LITERAL,
ONE_CHAR,
QMARK,
END_ANCHOR,
DOTS_SLASH,
NO_DOT,
NO_DOTS,
NO_DOT_SLASH,
NO_DOTS_SLASH,
QMARK_NO_DOT,
STAR,
START_ANCHOR
};
/**
* Windows glob regex
*/
const WINDOWS_CHARS = {
...POSIX_CHARS,
SLASH_LITERAL: `[${WIN_SLASH}]`,
QMARK: WIN_NO_SLASH,
STAR: `${WIN_NO_SLASH}*?`,
DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
NO_DOT: `(?!${DOT_LITERAL})`,
NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
END_ANCHOR: `(?:[${WIN_SLASH}]|$)`
};
/**
* POSIX Bracket Regex
*/
const POSIX_REGEX_SOURCE = {
alnum: 'a-zA-Z0-9',
alpha: 'a-zA-Z',
ascii: '\\x00-\\x7F',
blank: ' \\t',
cntrl: '\\x00-\\x1F\\x7F',
digit: '0-9',
graph: '\\x21-\\x7E',
lower: 'a-z',
print: '\\x20-\\x7E ',
punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~',
space: ' \\t\\r\\n\\v\\f',
upper: 'A-Z',
word: 'A-Za-z0-9_',
xdigit: 'A-Fa-f0-9'
};
module.exports = {
MAX_LENGTH: 1024 * 64,
POSIX_REGEX_SOURCE,
// regular expressions
REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
REGEX_NON_SPECIAL_CHAR: /^[^@![\].,$*+?^{}()|\\/]+/,
REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
// Replace globs with equivalent patterns to reduce parsing time.
REPLACEMENTS: {
'***': '*',
'**/**': '**',
'**/**/**': '**'
},
// Digits
CHAR_0: 48, /* 0 */
CHAR_9: 57, /* 9 */
// Alphabet chars.
CHAR_UPPERCASE_A: 65, /* A */
CHAR_LOWERCASE_A: 97, /* a */
CHAR_UPPERCASE_Z: 90, /* Z */
CHAR_LOWERCASE_Z: 122, /* z */
CHAR_LEFT_PARENTHESES: 40, /* ( */
CHAR_RIGHT_PARENTHESES: 41, /* ) */
CHAR_ASTERISK: 42, /* * */
// Non-alphabetic chars.
CHAR_AMPERSAND: 38, /* & */
CHAR_AT: 64, /* @ */
CHAR_BACKWARD_SLASH: 92, /* \ */
CHAR_CARRIAGE_RETURN: 13, /* \r */
CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */
CHAR_COLON: 58, /* : */
CHAR_COMMA: 44, /* , */
CHAR_DOT: 46, /* . */
CHAR_DOUBLE_QUOTE: 34, /* " */
CHAR_EQUAL: 61, /* = */
CHAR_EXCLAMATION_MARK: 33, /* ! */
CHAR_FORM_FEED: 12, /* \f */
CHAR_FORWARD_SLASH: 47, /* / */
CHAR_GRAVE_ACCENT: 96, /* ` */
CHAR_HASH: 35, /* # */
CHAR_HYPHEN_MINUS: 45, /* - */
CHAR_LEFT_ANGLE_BRACKET: 60, /* < */
CHAR_LEFT_CURLY_BRACE: 123, /* { */
CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */
CHAR_LINE_FEED: 10, /* \n */
CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */
CHAR_PERCENT: 37, /* % */
CHAR_PLUS: 43, /* + */
CHAR_QUESTION_MARK: 63, /* ? */
CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */
CHAR_RIGHT_CURLY_BRACE: 125, /* } */
CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */
CHAR_SEMICOLON: 59, /* ; */
CHAR_SINGLE_QUOTE: 39, /* ' */
CHAR_SPACE: 32, /* */
CHAR_TAB: 9, /* \t */
CHAR_UNDERSCORE: 95, /* _ */
CHAR_VERTICAL_LINE: 124, /* | */
CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */
SEP: path.sep,
/**
* Create EXTGLOB_CHARS
*/
extglobChars(chars) {
return {
'!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` },
'?': { type: 'qmark', open: '(?:', close: ')?' },
'+': { type: 'plus', open: '(?:', close: ')+' },
'*': { type: 'star', open: '(?:', close: ')*' },
'@': { type: 'at', open: '(?:', close: ')' }
};
},
/**
* Create GLOB_CHARS
*/
globChars(win32) {
return win32 === true ? WINDOWS_CHARS : POSIX_CHARS;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,315 @@
'use strict';
const path = require('path');
const scan = require('./scan');
const parse = require('./parse');
const utils = require('./utils');
/**
* 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.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch(glob[, options]);
*
* const isMatch = picomatch('*.!(*a)');
* console.log(isMatch('a.a')); //=> false
* console.log(isMatch('a.b')); //=> true
* ```
* @name picomatch
* @param {String|Array} `globs` One or more glob patterns.
* @param {Object=} `options`
* @return {Function=} Returns a matcher function.
* @api public
*/
const picomatch = (glob, options, returnState = false) => {
if (Array.isArray(glob)) {
let fns = glob.map(input => picomatch(input, options, returnState));
return str => {
for (let isMatch of fns) {
let state = isMatch(str);
if (state) return state;
}
return false;
};
}
if (typeof glob !== 'string' || glob === '') {
throw new TypeError('Expected pattern to be a non-empty string');
}
let opts = options || {};
let posix = utils.isWindows(options);
let regex = picomatch.makeRe(glob, options, false, true);
let state = regex.state;
delete regex.state;
let isIgnored = () => false;
if (opts.ignore) {
let ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null };
isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
}
const matcher = (input, returnObject = false) => {
let { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix });
let result = { glob, state, regex, posix, input, output, match, isMatch };
if (typeof opts.onResult === 'function') {
opts.onResult(result);
}
if (isMatch === false) {
result.isMatch = false;
return returnObject ? result : false;
}
if (isIgnored(input)) {
if (typeof opts.onIgnore === 'function') {
opts.onIgnore(result);
}
result.isMatch = false;
return returnObject ? result : false;
}
if (typeof opts.onMatch === 'function') {
opts.onMatch(result);
}
return returnObject ? result : true;
};
if (returnState) {
matcher.state = state;
}
return matcher;
};
/**
* Test `input` with the given `regex`. This is used by the main
* `picomatch()` function to test the input string.
*
* ```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' }
* ```
* @param {String} `input` String to test.
* @param {RegExp} `regex`
* @return {Object} Returns an object with matching info.
* @api public
*/
picomatch.test = (input, regex, options, { glob, posix } = {}) => {
if (typeof input !== 'string') {
throw new TypeError('Expected input to be a string');
}
if (input === '') {
return { isMatch: false, output: '' };
}
let opts = options || {};
let format = opts.format || (posix ? utils.toPosixSlashes : null);
let match = input === glob;
let output = (match && format) ? format(input) : input;
if (match === false) {
output = format ? format(input) : input;
match = output === glob;
}
if (match === false || opts.capture === true) {
if (opts.matchBase === true || opts.basename === true) {
match = picomatch.matchBase(input, regex, options, posix);
} else {
match = regex.exec(output);
}
}
return { isMatch: !!match, match, output };
};
/**
* Match the basename of a filepath.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.matchBase(input, glob[, options]);
* console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
* ```
* @param {String} `input` String to test.
* @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe).
* @return {Boolean}
* @api public
*/
picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => {
let regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
return regex.test(path.basename(input));
};
/**
* Returns true if **any** of the given glob `patterns` match the specified `string`.
*
* ```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
* ```
* @param {String|Array} str The string to test.
* @param {String|Array} patterns One or more glob patterns to use for matching.
* @param {Object} [options] See available [options](#options).
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
/**
* Parse a glob pattern to create the source string for a regular
* expression.
*
* ```js
* const picomatch = require('picomatch');
* const result = picomatch.parse(glob[, options]);
* ```
* @param {String} `glob`
* @param {Object} `options`
* @return {Object} Returns an object with useful properties and output to be used as a regex source string.
* @api public
*/
picomatch.parse = (glob, options) => parse(glob, options);
/**
* Scan a glob pattern to separate the pattern into segments.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.scan(input[, options]);
*
* const result = picomatch.scan('!./foo/*.js');
* console.log(result);
* // { prefix: '!./',
* // input: '!./foo/*.js',
* // base: 'foo',
* // glob: '*.js',
* // negated: true,
* // isGlob: true }
* ```
* @param {String} `input` Glob pattern to scan.
* @param {Object} `options`
* @return {Object} Returns an object with
* @api public
*/
picomatch.scan = (input, options) => scan(input, options);
/**
* Create a regular expression from a glob pattern.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.makeRe(input[, options]);
*
* console.log(picomatch.makeRe('*.js'));
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
* ```
* @param {String} `input` A glob pattern to convert to regex.
* @param {Object} `options`
* @return {RegExp} Returns a regex created from the given pattern.
* @api public
*/
picomatch.makeRe = (input, options, returnOutput = false, returnState = false) => {
if (!input || typeof input !== 'string') {
throw new TypeError('Expected a non-empty string');
}
let opts = options || {};
let prepend = opts.contains ? '' : '^';
let append = opts.contains ? '' : '$';
let state = { negated: false, fastpaths: true };
let prefix = '';
let output;
if (input.startsWith('./')) {
input = input.slice(2);
prefix = state.prefix = './';
}
if (opts.fastpaths !== false && (input[0] === '.' || input[0] === '*')) {
output = parse.fastpaths(input, options);
}
if (output === void 0) {
state = picomatch.parse(input, options);
state.prefix = prefix + (state.prefix || '');
output = state.output;
}
if (returnOutput === true) {
return output;
}
let source = `${prepend}(?:${output})${append}`;
if (state && state.negated === true) {
source = `^(?!${source}).*$`;
}
let regex = picomatch.toRegex(source, options);
if (returnState === true) {
regex.state = state;
}
return regex;
};
/**
* Create a regular expression from the given regex source string.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.toRegex(source[, options]);
*
* const { output } = picomatch.parse('*.js');
* console.log(picomatch.toRegex(output));
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
* ```
* @param {String} `source` Regular expression source string.
* @param {Object} `options`
* @return {RegExp}
* @api public
*/
picomatch.toRegex = (source, options) => {
try {
let opts = options || {};
return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
} catch (err) {
if (options && options.debug === true) throw err;
return /$^/;
}
};
/**
* Picomatch constants.
* @return {Object}
*/
picomatch.constants = require('./constants');
/**
* Expose "picomatch"
*/
module.exports = picomatch;

View File

@@ -0,0 +1,219 @@
'use strict';
const utils = require('./utils');
const {
CHAR_ASTERISK, /* * */
CHAR_AT, /* @ */
CHAR_BACKWARD_SLASH, /* \ */
CHAR_COMMA, /* , */
CHAR_DOT, /* . */
CHAR_EXCLAMATION_MARK, /* ! */
CHAR_FORWARD_SLASH, /* / */
CHAR_LEFT_CURLY_BRACE, /* { */
CHAR_LEFT_PARENTHESES, /* ( */
CHAR_LEFT_SQUARE_BRACKET, /* [ */
CHAR_PLUS, /* + */
CHAR_QUESTION_MARK, /* ? */
CHAR_RIGHT_CURLY_BRACE, /* } */
CHAR_RIGHT_PARENTHESES, /* ) */
CHAR_RIGHT_SQUARE_BRACKET /* ] */
} = require('./constants');
const isPathSeparator = code => {
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
};
/**
* Quickly scans a glob pattern and returns an object with a handful of
* useful properties, like `isGlob`, `path` (the leading non-glob, if it exists),
* `glob` (the actual pattern), and `negated` (true if the path starts with `!`).
*
* ```js
* const pm = require('picomatch');
* console.log(pm.scan('foo/bar/*.js'));
* { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' }
* ```
* @param {String} `str`
* @param {Object} `options`
* @return {Object} Returns an object with tokens and regex source string.
* @api public
*/
module.exports = (input, options) => {
let opts = options || {};
let length = input.length - 1;
let index = -1;
let start = 0;
let lastIndex = 0;
let isGlob = false;
let backslashes = false;
let negated = false;
let braces = 0;
let prev;
let code;
let braceEscaped = false;
let eos = () => index >= length;
let advance = () => {
prev = code;
return input.charCodeAt(++index);
};
while (index < length) {
code = advance();
let next;
if (code === CHAR_BACKWARD_SLASH) {
backslashes = true;
next = advance();
if (next === CHAR_LEFT_CURLY_BRACE) {
braceEscaped = true;
}
continue;
}
if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) {
braces++;
while (!eos() && (next = advance())) {
if (next === CHAR_BACKWARD_SLASH) {
backslashes = true;
next = advance();
continue;
}
if (next === CHAR_LEFT_CURLY_BRACE) {
braces++;
continue;
}
if (!braceEscaped && next === CHAR_DOT && (next = advance()) === CHAR_DOT) {
isGlob = true;
break;
}
if (!braceEscaped && next === CHAR_COMMA) {
isGlob = true;
break;
}
if (next === CHAR_RIGHT_CURLY_BRACE) {
braces--;
if (braces === 0) {
braceEscaped = false;
break;
}
}
}
}
if (code === CHAR_FORWARD_SLASH) {
if (prev === CHAR_DOT && index === (start + 1)) {
start += 2;
continue;
}
lastIndex = index + 1;
continue;
}
if (code === CHAR_ASTERISK) {
isGlob = true;
break;
}
if (code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK) {
isGlob = true;
break;
}
if (code === CHAR_LEFT_SQUARE_BRACKET) {
while (!eos() && (next = advance())) {
if (next === CHAR_BACKWARD_SLASH) {
backslashes = true;
next = advance();
continue;
}
if (next === CHAR_RIGHT_SQUARE_BRACKET) {
isGlob = true;
break;
}
}
}
let isExtglobChar = code === CHAR_PLUS
|| code === CHAR_AT
|| code === CHAR_EXCLAMATION_MARK;
if (isExtglobChar && input.charCodeAt(index + 1) === CHAR_LEFT_PARENTHESES) {
isGlob = true;
break;
}
if (code === CHAR_EXCLAMATION_MARK && index === start) {
negated = true;
start++;
continue;
}
if (code === CHAR_LEFT_PARENTHESES) {
while (!eos() && (next = advance())) {
if (next === CHAR_BACKWARD_SLASH) {
backslashes = true;
next = advance();
continue;
}
if (next === CHAR_RIGHT_PARENTHESES) {
isGlob = true;
break;
}
}
}
if (isGlob) {
break;
}
}
let prefix = '';
let orig = input;
let base = input;
let glob = '';
if (start > 0) {
prefix = input.slice(0, start);
input = input.slice(start);
lastIndex -= start;
}
if (base && isGlob === true && lastIndex > 0) {
base = input.slice(0, lastIndex);
glob = input.slice(lastIndex);
} else if (isGlob === true) {
base = '';
glob = input;
} else {
base = input;
}
if (base && base !== '' && base !== '/' && base !== input) {
if (isPathSeparator(base.charCodeAt(base.length - 1))) {
base = base.slice(0, -1);
}
}
if (opts.unescape === true) {
if (glob) glob = utils.removeBackslashes(glob);
if (base && backslashes === true) {
base = utils.removeBackslashes(base);
}
}
return { prefix, input: orig, base, glob, negated, isGlob };
};

View File

@@ -0,0 +1,43 @@
'use strict';
const path = require('path');
const win32 = process.platform === 'win32';
const {
REGEX_SPECIAL_CHARS,
REGEX_SPECIAL_CHARS_GLOBAL,
REGEX_REMOVE_BACKSLASH
} = 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(/\\/g, '/');
exports.removeBackslashes = str => {
return str.replace(REGEX_REMOVE_BACKSLASH, match => {
return match === '\\' ? '' : match;
});
}
exports.supportsLookbehinds = () => {
let segs = process.version.slice(1).split('.');
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) => {
let 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);
};

View File

@@ -0,0 +1,99 @@
{
"_args": [
[
"picomatch@2.0.7",
"D:\\code\\vscode-code-runner"
]
],
"_from": "picomatch@2.0.7",
"_id": "picomatch@2.0.7",
"_inBundle": false,
"_integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==",
"_location": "/picomatch",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "picomatch@2.0.7",
"name": "picomatch",
"escapedName": "picomatch",
"rawSpec": "2.0.7",
"saveSpec": null,
"fetchSpec": "2.0.7"
},
"_requiredBy": [
"/micromatch"
],
"_resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz",
"_spec": "2.0.7",
"_where": "D:\\code\\vscode-code-runner",
"author": {
"name": "Jon Schlinkert",
"url": "https://github.com/jonschlinkert"
},
"bugs": {
"url": "https://github.com/micromatch/picomatch/issues"
},
"description": "Blazing fast and accurate glob matcher written in JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.",
"devDependencies": {
"fill-range": "^7.0.1",
"gulp-format-md": "^2.0.0",
"mocha": "^6.0.2",
"nyc": "^13.3.0",
"time-require": "github:jonschlinkert/time-require"
},
"engines": {
"node": ">=8"
},
"files": [
"index.js",
"lib"
],
"homepage": "https://github.com/micromatch/picomatch",
"keywords": [
"glob",
"match",
"picomatch"
],
"license": "MIT",
"main": "index.js",
"name": "picomatch",
"repository": {
"type": "git",
"url": "git+https://github.com/micromatch/picomatch.git"
},
"scripts": {
"cover": "nyc --reporter=text --reporter=html mocha",
"test": "mocha"
},
"verb": {
"toc": false,
"layout": false,
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"related": {
"list": [
"micromatch",
"braces"
]
},
"reflinks": [
"braces",
"expand-brackets",
"extglob",
"fill-range",
"micromatch",
"minimatch",
"nanomatch",
"picomatch"
]
},
"version": "2.0.7"
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-present, Jon Schlinkert.
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.

View File

@@ -0,0 +1,305 @@
# to-regex-range [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/to-regex-range.svg?style=flat)](https://www.npmjs.com/package/to-regex-range) [![NPM monthly downloads](https://img.shields.io/npm/dm/to-regex-range.svg?style=flat)](https://npmjs.org/package/to-regex-range) [![NPM total downloads](https://img.shields.io/npm/dt/to-regex-range.svg?style=flat)](https://npmjs.org/package/to-regex-range) [![Linux Build Status](https://img.shields.io/travis/micromatch/to-regex-range.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/to-regex-range)
> Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.78 million test assertions.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save to-regex-range
```
<details>
<summary><strong>What does this do?</strong></summary>
<br>
This libary generates the `source` string to be passed to `new RegExp()` for matching a range of numbers.
**Example**
```js
const toRegexRange = require('to-regex-range');
const regex = new RegExp(toRegexRange('15', '95'));
```
A string is returned so that you can do whatever you need with it before passing it to `new RegExp()` (like adding `^` or `$` boundaries, defining flags, or combining it another string).
<br>
</details>
<details>
<summary><strong>Why use this library?</strong></summary>
<br>
### Convenience
Creating regular expressions for matching numbers gets deceptively complicated pretty fast.
For example, let's say you need a validation regex for matching part of a user-id, postal code, social security number, tax id, etc:
* regex for matching `1` => `/1/` (easy enough)
* regex for matching `1` through `5` => `/[1-5]/` (not bad...)
* regex for matching `1` or `5` => `/(1|5)/` (still easy...)
* regex for matching `1` through `50` => `/([1-9]|[1-4][0-9]|50)/` (uh-oh...)
* regex for matching `1` through `55` => `/([1-9]|[1-4][0-9]|5[0-5])/` (no prob, I can do this...)
* regex for matching `1` through `555` => `/([1-9]|[1-9][0-9]|[1-4][0-9]{2}|5[0-4][0-9]|55[0-5])/` (maybe not...)
* regex for matching `0001` through `5555` => `/(0{3}[1-9]|0{2}[1-9][0-9]|0[1-9][0-9]{2}|[1-4][0-9]{3}|5[0-4][0-9]{2}|55[0-4][0-9]|555[0-5])/` (okay, I get the point!)
The numbers are contrived, but they're also really basic. In the real world you might need to generate a regex on-the-fly for validation.
**Learn more**
If you're interested in learning more about [character classes](http://www.regular-expressions.info/charclass.html) and other regex features, I personally have always found [regular-expressions.info](http://www.regular-expressions.info/charclass.html) to be pretty useful.
### Heavily tested
As of April 07, 2019, this library runs [>1m test assertions](./test/test.js) against generated regex-ranges to provide brute-force verification that results are correct.
Tests run in ~280ms on my MacBook Pro, 2.5 GHz Intel Core i7.
### Optimized
Generated regular expressions are optimized:
* duplicate sequences and character classes are reduced using quantifiers
* smart enough to use `?` conditionals when number(s) or range(s) can be positive or negative
* uses fragment caching to avoid processing the same exact string more than once
<br>
</details>
## Usage
Add this library to your javascript application with the following line of code
```js
const toRegexRange = require('to-regex-range');
```
The main export is a function that takes two integers: the `min` value and `max` value (formatted as strings or numbers).
```js
const source = toRegexRange('15', '95');
//=> 1[5-9]|[2-8][0-9]|9[0-5]
const regex = new RegExp(`^${source}$`);
console.log(regex.test('14')); //=> false
console.log(regex.test('50')); //=> true
console.log(regex.test('94')); //=> true
console.log(regex.test('96')); //=> false
```
## Options
### options.capture
**Type**: `boolean`
**Deafault**: `undefined`
Wrap the returned value in parentheses when there is more than one regex condition. Useful when you're dynamically generating ranges.
```js
console.log(toRegexRange('-10', '10'));
//=> -[1-9]|-?10|[0-9]
console.log(toRegexRange('-10', '10', { capture: true }));
//=> (-[1-9]|-?10|[0-9])
```
### options.shorthand
**Type**: `boolean`
**Deafault**: `undefined`
Use the regex shorthand for `[0-9]`:
```js
console.log(toRegexRange('0', '999999'));
//=> [0-9]|[1-9][0-9]{1,5}
console.log(toRegexRange('0', '999999', { shorthand: true }));
//=> \d|[1-9]\d{1,5}
```
### options.relaxZeros
**Type**: `boolean`
**Default**: `true`
This option relaxes matching for leading zeros when when ranges are zero-padded.
```js
const source = toRegexRange('-0010', '0010');
const regex = new RegExp(`^${source}$`);
console.log(regex.test('-10')); //=> true
console.log(regex.test('-010')); //=> true
console.log(regex.test('-0010')); //=> true
console.log(regex.test('10')); //=> true
console.log(regex.test('010')); //=> true
console.log(regex.test('0010')); //=> true
```
When `relaxZeros` is false, matching is strict:
```js
const source = toRegexRange('-0010', '0010', { relaxZeros: false });
const regex = new RegExp(`^${source}$`);
console.log(regex.test('-10')); //=> false
console.log(regex.test('-010')); //=> false
console.log(regex.test('-0010')); //=> true
console.log(regex.test('10')); //=> false
console.log(regex.test('010')); //=> false
console.log(regex.test('0010')); //=> true
```
## Examples
| **Range** | **Result** | **Compile time** |
| --- | --- | --- |
| `toRegexRange(-10, 10)` | `-[1-9]\|-?10\|[0-9]` | _132μs_ |
| `toRegexRange(-100, -10)` | `-1[0-9]\|-[2-9][0-9]\|-100` | _50μs_ |
| `toRegexRange(-100, 100)` | `-[1-9]\|-?[1-9][0-9]\|-?100\|[0-9]` | _42μs_ |
| `toRegexRange(001, 100)` | `0{0,2}[1-9]\|0?[1-9][0-9]\|100` | _109μs_ |
| `toRegexRange(001, 555)` | `0{0,2}[1-9]\|0?[1-9][0-9]\|[1-4][0-9]{2}\|5[0-4][0-9]\|55[0-5]` | _51μs_ |
| `toRegexRange(0010, 1000)` | `0{0,2}1[0-9]\|0{0,2}[2-9][0-9]\|0?[1-9][0-9]{2}\|1000` | _31μs_ |
| `toRegexRange(1, 50)` | `[1-9]\|[1-4][0-9]\|50` | _24μs_ |
| `toRegexRange(1, 55)` | `[1-9]\|[1-4][0-9]\|5[0-5]` | _23μs_ |
| `toRegexRange(1, 555)` | `[1-9]\|[1-9][0-9]\|[1-4][0-9]{2}\|5[0-4][0-9]\|55[0-5]` | _30μs_ |
| `toRegexRange(1, 5555)` | `[1-9]\|[1-9][0-9]{1,2}\|[1-4][0-9]{3}\|5[0-4][0-9]{2}\|55[0-4][0-9]\|555[0-5]` | _43μs_ |
| `toRegexRange(111, 555)` | `11[1-9]\|1[2-9][0-9]\|[2-4][0-9]{2}\|5[0-4][0-9]\|55[0-5]` | _38μs_ |
| `toRegexRange(29, 51)` | `29\|[34][0-9]\|5[01]` | _24μs_ |
| `toRegexRange(31, 877)` | `3[1-9]\|[4-9][0-9]\|[1-7][0-9]{2}\|8[0-6][0-9]\|87[0-7]` | _32μs_ |
| `toRegexRange(5, 5)` | `5` | _8μs_ |
| `toRegexRange(5, 6)` | `5\|6` | _11μs_ |
| `toRegexRange(1, 2)` | `1\|2` | _6μs_ |
| `toRegexRange(1, 5)` | `[1-5]` | _15μs_ |
| `toRegexRange(1, 10)` | `[1-9]\|10` | _22μs_ |
| `toRegexRange(1, 100)` | `[1-9]\|[1-9][0-9]\|100` | _25μs_ |
| `toRegexRange(1, 1000)` | `[1-9]\|[1-9][0-9]{1,2}\|1000` | _31μs_ |
| `toRegexRange(1, 10000)` | `[1-9]\|[1-9][0-9]{1,3}\|10000` | _34μs_ |
| `toRegexRange(1, 100000)` | `[1-9]\|[1-9][0-9]{1,4}\|100000` | _36μs_ |
| `toRegexRange(1, 1000000)` | `[1-9]\|[1-9][0-9]{1,5}\|1000000` | _42μs_ |
| `toRegexRange(1, 10000000)` | `[1-9]\|[1-9][0-9]{1,6}\|10000000` | _42μs_ |
## Heads up!
**Order of arguments**
When the `min` is larger than the `max`, values will be flipped to create a valid range:
```js
toRegexRange('51', '29');
```
Is effectively flipped to:
```js
toRegexRange('29', '51');
//=> 29|[3-4][0-9]|5[0-1]
```
**Steps / increments**
This library does not support steps (increments). A pr to add support would be welcome.
## History
### v2.0.0 - 2017-04-21
**New features**
Adds support for zero-padding!
### v1.0.0
**Optimizations**
Repeating ranges are now grouped using quantifiers. rocessing time is roughly the same, but the generated regex is much smaller, which should result in faster matching.
## Attribution
Inspired by the python library [range-regex](https://github.com/dimka665/range-regex).
## 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).
</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>
### Related projects
You might also be interested in these projects:
* [expand-range](https://www.npmjs.com/package/expand-range): Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. Used… [more](https://github.com/jonschlinkert/expand-range) | [homepage](https://github.com/jonschlinkert/expand-range "Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. Used by micromatch.")
* [fill-range](https://www.npmjs.com/package/fill-range): Fill in a range of numbers or letters, optionally passing an increment or `step` to… [more](https://github.com/jonschlinkert/fill-range) | [homepage](https://github.com/jonschlinkert/fill-range "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`")
* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/micromatch/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
* [repeat-element](https://www.npmjs.com/package/repeat-element): Create an array by repeating the given value n times. | [homepage](https://github.com/jonschlinkert/repeat-element "Create an array by repeating the given value n times.")
* [repeat-string](https://www.npmjs.com/package/repeat-string): Repeat the given string n times. Fastest implementation for repeating a string. | [homepage](https://github.com/jonschlinkert/repeat-string "Repeat the given string n times. Fastest implementation for repeating a string.")
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 63 | [jonschlinkert](https://github.com/jonschlinkert) |
| 3 | [doowb](https://github.com/doowb) |
| 2 | [realityking](https://github.com/realityking) |
### Author
**Jon Schlinkert**
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
Please consider supporting me on Patreon, or [start your own Patreon page](https://patreon.com/invite/bxpbvm)!
<a href="https://www.patreon.com/jonschlinkert">
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" height="50">
</a>
### License
Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 07, 2019._

View File

@@ -0,0 +1,288 @@
/*!
* to-regex-range <https://github.com/micromatch/to-regex-range>
*
* Copyright (c) 2015-present, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
const isNumber = require('is-number');
const toRegexRange = (min, max, options) => {
if (isNumber(min) === false) {
throw new TypeError('toRegexRange: expected the first argument to be a number');
}
if (max === void 0 || min === max) {
return String(min);
}
if (isNumber(max) === false) {
throw new TypeError('toRegexRange: expected the second argument to be a number.');
}
let opts = { relaxZeros: true, ...options };
if (typeof opts.strictZeros === 'boolean') {
opts.relaxZeros = opts.strictZeros === false;
}
let relax = String(opts.relaxZeros);
let shorthand = String(opts.shorthand);
let capture = String(opts.capture);
let wrap = String(opts.wrap);
let cacheKey = min + ':' + max + '=' + relax + shorthand + capture + wrap;
if (toRegexRange.cache.hasOwnProperty(cacheKey)) {
return toRegexRange.cache[cacheKey].result;
}
let a = Math.min(min, max);
let b = Math.max(min, max);
if (Math.abs(a - b) === 1) {
let result = min + '|' + max;
if (opts.capture) {
return `(${result})`;
}
if (opts.wrap === false) {
return result;
}
return `(?:${result})`;
}
let isPadded = hasPadding(min) || hasPadding(max);
let state = { min, max, a, b };
let positives = [];
let negatives = [];
if (isPadded) {
state.isPadded = isPadded;
state.maxLen = String(state.max).length;
}
if (a < 0) {
let newMin = b < 0 ? Math.abs(b) : 1;
negatives = splitToPatterns(newMin, Math.abs(a), state, opts);
a = state.a = 0;
}
if (b >= 0) {
positives = splitToPatterns(a, b, state, opts);
}
state.negatives = negatives;
state.positives = positives;
state.result = collatePatterns(negatives, positives, opts);
if (opts.capture === true) {
state.result = `(${state.result})`;
} else if (opts.wrap !== false && (positives.length + negatives.length) > 1) {
state.result = `(?:${state.result})`;
}
toRegexRange.cache[cacheKey] = state;
return state.result;
};
function collatePatterns(neg, pos, options) {
let onlyNegative = filterPatterns(neg, pos, '-', false, options) || [];
let onlyPositive = filterPatterns(pos, neg, '', false, options) || [];
let intersected = filterPatterns(neg, pos, '-?', true, options) || [];
let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive);
return subpatterns.join('|');
}
function splitToRanges(min, max) {
let nines = 1;
let zeros = 1;
let stop = countNines(min, nines);
let stops = new Set([max]);
while (min <= stop && stop <= max) {
stops.add(stop);
nines += 1;
stop = countNines(min, nines);
}
stop = countZeros(max + 1, zeros) - 1;
while (min < stop && stop <= max) {
stops.add(stop);
zeros += 1;
stop = countZeros(max + 1, zeros) - 1;
}
stops = [...stops];
stops.sort(compare);
return stops;
}
/**
* Convert a range to a regex pattern
* @param {Number} `start`
* @param {Number} `stop`
* @return {String}
*/
function rangeToPattern(start, stop, options) {
if (start === stop) {
return { pattern: start, count: [], digits: 0 };
}
let zipped = zip(start, stop);
let digits = zipped.length;
let pattern = '';
let count = 0;
for (let i = 0; i < digits; i++) {
let [startDigit, stopDigit] = zipped[i];
if (startDigit === stopDigit) {
pattern += startDigit;
} else if (startDigit !== '0' || stopDigit !== '9') {
pattern += toCharacterClass(startDigit, stopDigit, options);
} else {
count++;
}
}
if (count) {
pattern += options.shorthand === true ? '\\d' : '[0-9]';
}
return { pattern, count: [count], digits };
}
function splitToPatterns(min, max, tok, options) {
let ranges = splitToRanges(min, max);
let tokens = [];
let start = min;
let prev;
for (let i = 0; i < ranges.length; i++) {
let max = ranges[i];
let obj = rangeToPattern(String(start), String(max), options);
let zeros = '';
if (!tok.isPadded && prev && prev.pattern === obj.pattern) {
if (prev.count.length > 1) {
prev.count.pop();
}
prev.count.push(obj.count[0]);
prev.string = prev.pattern + toQuantifier(prev.count);
start = max + 1;
continue;
}
if (tok.isPadded) {
zeros = padZeros(max, tok, options);
}
obj.string = zeros + obj.pattern + toQuantifier(obj.count);
tokens.push(obj);
start = max + 1;
prev = obj;
}
return tokens;
}
function filterPatterns(arr, comparison, prefix, intersection, options) {
let result = [];
for (let ele of arr) {
let { string } = ele;
// only push if _both_ are negative...
if (!intersection && !contains(comparison, 'string', string)) {
result.push(prefix + string);
}
// or _both_ are positive
if (intersection && contains(comparison, 'string', string)) {
result.push(prefix + string);
}
}
return result;
}
/**
* Zip strings
*/
function zip(a, b) {
let arr = [];
for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]);
return arr;
}
function compare(a, b) {
return a > b ? 1 : b > a ? -1 : 0;
}
function contains(arr, key, val) {
return arr.some(ele => ele[key] === val);
}
function countNines(min, len) {
return Number(String(min).slice(0, -len) + '9'.repeat(len));
}
function countZeros(integer, zeros) {
return integer - (integer % Math.pow(10, zeros));
}
function toQuantifier(digits) {
let [start = 0, stop = ''] = digits;
if (stop || start > 1) {
return `{${start + (stop ? ',' + stop : '')}}`;
}
return '';
}
function toCharacterClass(a, b, options) {
return `[${a}${(b - a === 1) ? '' : '-'}${b}]`;
}
function hasPadding(str) {
return /^-?(0+)\d/.test(str);
}
function padZeros(value, tok, options) {
if (!tok.isPadded) {
return value;
}
let diff = Math.abs(tok.maxLen - String(value).length);
let relax = options.relaxZeros !== false;
switch (diff) {
case 0:
return '';
case 1:
return relax ? '0?' : '0';
case 2:
return relax ? '0{0,2}' : '00';
default: {
return relax ? `0{0,${diff}}` : `0{${diff}}`;
}
}
}
/**
* Cache
*/
toRegexRange.cache = {};
toRegexRange.clearCache = () => (toRegexRange.cache = {});
/**
* Expose `toRegexRange`
*/
module.exports = toRegexRange;

View File

@@ -0,0 +1,128 @@
{
"_args": [
[
"to-regex-range@5.0.1",
"D:\\code\\vscode-code-runner"
]
],
"_from": "to-regex-range@5.0.1",
"_id": "to-regex-range@5.0.1",
"_inBundle": false,
"_integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"_location": "/to-regex-range",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "to-regex-range@5.0.1",
"name": "to-regex-range",
"escapedName": "to-regex-range",
"rawSpec": "5.0.1",
"saveSpec": null,
"fetchSpec": "5.0.1"
},
"_requiredBy": [
"/fill-range"
],
"_resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"_spec": "5.0.1",
"_where": "D:\\code\\vscode-code-runner",
"author": {
"name": "Jon Schlinkert",
"url": "https://github.com/jonschlinkert"
},
"bugs": {
"url": "https://github.com/micromatch/to-regex-range/issues"
},
"contributors": [
{
"name": "Jon Schlinkert",
"url": "http://twitter.com/jonschlinkert"
},
{
"name": "Rouven Weßling",
"url": "www.rouvenwessling.de"
}
],
"dependencies": {
"is-number": "^7.0.0"
},
"description": "Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.78 million test assertions.",
"devDependencies": {
"fill-range": "^6.0.0",
"gulp-format-md": "^2.0.0",
"mocha": "^6.0.2",
"text-table": "^0.2.0",
"time-diff": "^0.3.1"
},
"engines": {
"node": ">=8.0"
},
"files": [
"index.js"
],
"homepage": "https://github.com/micromatch/to-regex-range",
"keywords": [
"bash",
"date",
"expand",
"expansion",
"expression",
"glob",
"match",
"match date",
"match number",
"match numbers",
"match year",
"matches",
"matching",
"number",
"numbers",
"numerical",
"range",
"ranges",
"regex",
"regexp",
"regular",
"regular expression",
"sequence"
],
"license": "MIT",
"main": "index.js",
"name": "to-regex-range",
"repository": {
"type": "git",
"url": "git+https://github.com/micromatch/to-regex-range.git"
},
"scripts": {
"test": "mocha"
},
"verb": {
"layout": "default",
"toc": false,
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"helpers": {
"examples": {
"displayName": "examples"
}
},
"related": {
"list": [
"expand-range",
"fill-range",
"micromatch",
"repeat-element",
"repeat-string"
]
}
},
"version": "5.0.1"
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Peter Krumins
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.

View File

@@ -0,0 +1,89 @@
Tree Kill
=========
Kill all processes in the process tree, including the root process.
Examples
=======
Kill all the descendent processes of the process with pid `1`, including the process with pid `1` itself:
```js
var kill = require('tree-kill');
kill(1);
```
Send a signal other than SIGTERM.:
```js
var kill = require('tree-kill');
kill(1, 'SIGKILL');
```
Run a callback when done killing the processes. Passes an error argument if there was an error.
```js
var kill = require('tree-kill');
kill(1, 'SIGKILL', function(err) {
// Do things
});
```
You can also install tree-kill globally and use it as a command:
```sh
tree-kill 1 # sends SIGTERM to process 1 and its descendents
tree-kill 1 SIGTERM # same
tree-kill 1 SIGKILL # sends KILL instead of TERMINATE
```
Methods
=======
## require('tree-kill')(pid, [signal], [callback]);
Sends signal `signal` to all children processes of the process with pid `pid`, including `pid`. Signal defaults to `SIGTERM`.
For Linux, this uses `ps -o pid --no-headers --ppid PID` to find the parent pids of `PID`.
For Darwin/OSX, this uses `pgrep -P PID` to find the parent pids of `PID`.
For Windows, this uses `'taskkill /pid PID /T /F'` to kill the process tree. Note that on Windows, sending the different kinds of POSIX signals is not possible.
Install
=======
With [npm](https://npmjs.org) do:
```
npm install tree-kill
```
License
=======
MIT
Changelog
=========
## [1.2.2] - 2019-12-11
### Changed
- security fix: sanitize `pid` parameter to fix arbitrary code execution vulnerability
## [1.2.1] - 2018-11-05
### Changed
- added missing LICENSE file
- updated TypeScript definitions
## [1.2.0] - 2017-09-19
### Added
- TypeScript definitions
### Changed
- `kill(pid, callback)` works. Before you had to use `kill(pid, signal, callback)`
## [1.1.0] - 2016-05-13
### Added
- A `tree-kill` CLI
## [1.0.0] - 2015-09-17
### Added
- optional callback
- Darwin support

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env node
kill = require('.')
try {
kill(process.argv[2], process.argv[3], function(err){
if (err) {
console.log(err.message)
process.exit(1)
}
})
}
catch (err) {
console.log(err.message)
process.exit(1)
}

View File

@@ -0,0 +1,13 @@
/**
* Kills process identified by `pid` and all its children
*
* @param pid
* @param signal 'SIGTERM' by default
* @param callback
*/
declare function treeKill(pid: number, callback?: (error?: Error) => void): void;
declare function treeKill(pid: number, signal?: string | number, callback?: (error?: Error) => void): void;
declare namespace treeKill {}
export = treeKill;

View File

@@ -0,0 +1,118 @@
'use strict';
var childProcess = require('child_process');
var spawn = childProcess.spawn;
var exec = childProcess.exec;
module.exports = function (pid, signal, callback) {
if (typeof signal === 'function' && callback === undefined) {
callback = signal;
signal = undefined;
}
pid = parseInt(pid);
if (Number.isNaN(pid)) {
if (callback) {
return callback(new Error("pid must be a number"));
} else {
throw new Error("pid must be a number");
}
}
var tree = {};
var pidsToProcess = {};
tree[pid] = [];
pidsToProcess[pid] = 1;
switch (process.platform) {
case 'win32':
exec('taskkill /pid ' + pid + ' /T /F', callback);
break;
case 'darwin':
buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
return spawn('pgrep', ['-P', parentPid]);
}, function () {
killAll(tree, signal, callback);
});
break;
// case 'sunos':
// buildProcessTreeSunOS(pid, tree, pidsToProcess, function () {
// killAll(tree, signal, callback);
// });
// break;
default: // Linux
buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {
return spawn('ps', ['-o', 'pid', '--no-headers', '--ppid', parentPid]);
}, function () {
killAll(tree, signal, callback);
});
break;
}
};
function killAll (tree, signal, callback) {
var killed = {};
try {
Object.keys(tree).forEach(function (pid) {
tree[pid].forEach(function (pidpid) {
if (!killed[pidpid]) {
killPid(pidpid, signal);
killed[pidpid] = 1;
}
});
if (!killed[pid]) {
killPid(pid, signal);
killed[pid] = 1;
}
});
} catch (err) {
if (callback) {
return callback(err);
} else {
throw err;
}
}
if (callback) {
return callback();
}
}
function killPid(pid, signal) {
try {
process.kill(parseInt(pid, 10), signal);
}
catch (err) {
if (err.code !== 'ESRCH') throw err;
}
}
function buildProcessTree (parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) {
var ps = spawnChildProcessesList(parentPid);
var allData = '';
ps.stdout.on('data', function (data) {
var data = data.toString('ascii');
allData += data;
});
var onClose = function (code) {
delete pidsToProcess[parentPid];
if (code != 0) {
// no more parent processes
if (Object.keys(pidsToProcess).length == 0) {
cb();
}
return;
}
allData.match(/\d+/g).forEach(function (pid) {
pid = parseInt(pid, 10);
tree[parentPid].push(pid);
tree[pid] = [];
pidsToProcess[pid] = 1;
buildProcessTree(pid, tree, pidsToProcess, spawnChildProcessesList, cb);
});
};
ps.on('close', onClose);
}

View File

@@ -0,0 +1,82 @@
{
"_args": [
[
"tree-kill@1.2.2",
"D:\\code\\vscode-code-runner"
]
],
"_from": "tree-kill@1.2.2",
"_id": "tree-kill@1.2.2",
"_inBundle": false,
"_integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"_location": "/tree-kill",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "tree-kill@1.2.2",
"name": "tree-kill",
"escapedName": "tree-kill",
"rawSpec": "1.2.2",
"saveSpec": null,
"fetchSpec": "1.2.2"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"_spec": "1.2.2",
"_where": "D:\\code\\vscode-code-runner",
"author": {
"name": "Peteris Krumins",
"email": "peteris.krumins@gmail.com",
"url": "http://www.catonmat.net"
},
"bin": {
"tree-kill": "cli.js"
},
"bugs": {
"url": "https://github.com/pkrumins/node-tree-kill/issues"
},
"contributors": [
{
"name": "Todd Wolfson",
"email": "todd@twolfson.com",
"url": "http://twolfson.com/"
},
{
"name": "William Hilton",
"email": "wmhilton@gmail.com",
"url": "http://wmhilton.com/"
},
{
"name": "Fabrício Matté",
"url": "http://ultcombo.js.org/"
}
],
"description": "kill trees of processes",
"devDependencies": {
"mocha": "^2.2.5"
},
"homepage": "https://github.com/pkrumins/node-tree-kill",
"keywords": [
"tree",
"trees",
"process",
"processes",
"kill",
"signal"
],
"license": "MIT",
"main": "index.js",
"name": "tree-kill",
"repository": {
"type": "git",
"url": "git://github.com/pkrumins/node-tree-kill.git"
},
"scripts": {
"test": "mocha"
},
"types": "index.d.ts",
"version": "1.2.2"
}

View File

@@ -0,0 +1,760 @@
<a name="0.7.6"></a>
## [0.7.6](https://github.com/angular/zone.js/compare/v0.7.4...0.7.6) (2017-01-17)
### Bug Fixes
* **doc:** typo in comment and reformat README.md ([#590](https://github.com/angular/zone.js/issues/590)) ([95ad315](https://github.com/angular/zone.js/commit/95ad315))
* **ZoneAwareError:** Error should keep prototype chain and can be called without new ([82722c3](https://github.com/angular/zone.js/commit/82722c3)), closes [#546](https://github.com/angular/zone.js/issues/546) [#554](https://github.com/angular/zone.js/issues/554) [#555](https://github.com/angular/zone.js/issues/555)
* [#536](https://github.com/angular/zone.js/issues/536), add notification api patch ([#599](https://github.com/angular/zone.js/issues/599)) ([83dfa97](https://github.com/angular/zone.js/commit/83dfa97))
* [#593](https://github.com/angular/zone.js/issues/593), only call removeAttribute when have the method ([#594](https://github.com/angular/zone.js/issues/594)) ([1401d60](https://github.com/angular/zone.js/commit/1401d60))
* [#595](https://github.com/angular/zone.js/issues/595), refactor ZoneAwareError property copy ([#597](https://github.com/angular/zone.js/issues/597)) ([f7330de](https://github.com/angular/zone.js/commit/f7330de))
* [#604](https://github.com/angular/zone.js/issues/604), sometimes setInterval test spec will fail on Android 4.4 ([#605](https://github.com/angular/zone.js/issues/605)) ([e3cd1f4](https://github.com/angular/zone.js/commit/e3cd1f4))
* add missing test MutationObserver ([5c7bc01](https://github.com/angular/zone.js/commit/5c7bc01))
* Promise.toString() to look like native function ([f854ce0](https://github.com/angular/zone.js/commit/f854ce0))
<a name="0.7.5"></a>
## [0.7.5](https://github.com/angular/zone.js/compare/v0.7.4...0.7.5) (2017-01-12)
### Bug Fixes
* patch fs methods as macrotask, add test cases of fs watcher ([#572](https://github.com/angular/zone.js/issues/572)) ([e1d3240](https://github.com/angular/zone.js/commit/e1d3240))
* fix [#577](https://github.com/angular/zone.js/issues/577), canPatchViaPropertyDescriptor test should add configurable to XMLHttpRequest.prototype ([#578](https://github.com/angular/zone.js/issues/578)) ([c297752](https://github.com/angular/zone.js/commit/c297752))
* fix [#551](https://github.com/angular/zone.js/issues/551), add toJSON to ZoneTask to prevent cyclic error ([#576](https://github.com/angular/zone.js/issues/576)) ([03d19f9](https://github.com/angular/zone.js/commit/03d19f9))
* fix [#574](https://github.com/angular/zone.js/issues/574), captureStackTrace will have additional stackframe from Zone will break binding.js ([#575](https://github.com/angular/zone.js/issues/575)) ([41f5306](https://github.com/angular/zone.js/commit/41f5306))
* fix [#569](https://github.com/angular/zone.js/issues/569), request will cause updateTaskCount failed if we call abort multipletimes ([#570](https://github.com/angular/zone.js/issues/570)) ([62f1449](https://github.com/angular/zone.js/commit/62f1449))
* add web-api.ts to patch mediaQuery ([#571](https://github.com/angular/zone.js/issues/571)) ([e92f934](https://github.com/angular/zone.js/commit/e92f934))
* fix [#584](https://github.com/angular/zone.js/issues/584), remove android 4.1~4.3, add no-ssl options to make android 4.4 pass test ([#586](https://github.com/angular/zone.js/issues/586)) ([7cd570e](https://github.com/angular/zone.js/commit/7cd570e))
* Fix [#532](https://github.com/angular/zone.js/issues/532), Fix [#566](https://github.com/angular/zone.js/issues/566), add tslint in ci, add tslint/format/test/karma in precommit of git ([#565](https://github.com/angular/zone.js/issues/565)) ([fb8d51c](https://github.com/angular/zone.js/commit/fb8d51c))
* docs(zone.ts): fix typo ([#583](https://github.com/angular/zone.js/issues/583)) ([ecbef87](https://github.com/angular/zone.js/commit/ecbef87))
* add missing test MutationObserver ([5c7bc01](https://github.com/angular/zone.js/commit/5c7bc01))
* Promise.toString() to look like native function ([f854ce0](https://github.com/angular/zone.js/commit/f854ce0))
* **ZoneAwareError:** Error should keep prototype chain and can be called without new ([82722c3](https://github.com/angular/zone.js/commit/82722c3)), closes [#546](https://github.com/angular/zone.js/issues/546) [#554](https://github.com/angular/zone.js/issues/554) [#555](https://github.com/angular/zone.js/issues/555)
<a name="0.7.4"></a>
## [0.7.4](https://github.com/angular/zone.js/compare/v0.7.1...0.7.4) (2016-12-31)
### Bug Fixes
* add better Type safety ([610649b](https://github.com/angular/zone.js/commit/610649b))
* add missing test MutationObserver ([5c7bc01](https://github.com/angular/zone.js/commit/5c7bc01))
* correct currentZone passed into delegate methods ([dc12d8e](https://github.com/angular/zone.js/commit/dc12d8e)), closes [#587](https://github.com/angular/zone.js/issues/587) [#539](https://github.com/angular/zone.js/issues/539)
* correct zone.min.js not including zone ([384f5ec](https://github.com/angular/zone.js/commit/384f5ec))
* Correct ZoneAwareError prototype chain ([ba7858c](https://github.com/angular/zone.js/commit/ba7858c)), closes [#546](https://github.com/angular/zone.js/issues/546) [#547](https://github.com/angular/zone.js/issues/547)
* formatting issue. ([c70e9ec](https://github.com/angular/zone.js/commit/c70e9ec))
* inline event handler issue ([20b5a5d](https://github.com/angular/zone.js/commit/20b5a5d)), closes [#525](https://github.com/angular/zone.js/issues/525) [#540](https://github.com/angular/zone.js/issues/540)
* parameterize `wrap` method on `Zone` ([#542](https://github.com/angular/zone.js/issues/542)) ([f522e1b](https://github.com/angular/zone.js/commit/f522e1b))
* **closure:** avoid property renaming on globals ([af14646](https://github.com/angular/zone.js/commit/af14646))
* Prevent adding listener for xhrhttprequest multiple times ([9509747](https://github.com/angular/zone.js/commit/9509747)), closes [#529](https://github.com/angular/zone.js/issues/529) [#527](https://github.com/angular/zone.js/issues/527) [#287](https://github.com/angular/zone.js/issues/287) [#530](https://github.com/angular/zone.js/issues/530)
* Promise.toString() to look like native function ([f854ce0](https://github.com/angular/zone.js/commit/f854ce0))
* **closure:** Fix closure error suppression comment. ([#552](https://github.com/angular/zone.js/issues/552)) ([2643783](https://github.com/angular/zone.js/commit/2643783))
* Run tests on both the build as well as the dist folder ([#514](https://github.com/angular/zone.js/issues/514)) ([c0604f5](https://github.com/angular/zone.js/commit/c0604f5))
* support nw.js environment ([486010b](https://github.com/angular/zone.js/commit/486010b)), closes [#524](https://github.com/angular/zone.js/issues/524)
### Features
* Patch captureStackTrace/prepareStackTrace to ZoneAwareError, patch process.nextTick, fix removeAllListeners bug ([#516](https://github.com/angular/zone.js/issues/516)) ([c36c0bc](https://github.com/angular/zone.js/commit/c36c0bc)), closes [#484](https://github.com/angular/zone.js/issues/484) [#491](https://github.com/angular/zone.js/issues/491)
<a name="0.7.1"></a>
## [0.7.1](https://github.com/angular/zone.js/compare/v0.7.0...v0.7.1) (2016-11-22)
### Bug Fixes
* missing zone from the build file ([e961833](https://github.com/angular/zone.js/commit/e961833))
<a name="0.7.0"></a>
# [0.7.0](https://github.com/angular/zone.js/compare/0.6.25...v0.7.0) (2016-11-22)
### Bug Fixes
* **node:** crash when calling listeners() for event with no listeners ([431f6f0](https://github.com/angular/zone.js/commit/431f6f0))
* support clearing the timeouts with numeric IDs ([fea6d68](https://github.com/angular/zone.js/commit/fea6d68)), closes [#461](https://github.com/angular/zone.js/issues/461)
* **promise:** include stack trace in an unhandlerd promise ([#463](https://github.com/angular/zone.js/issues/463)) ([737f8d8](https://github.com/angular/zone.js/commit/737f8d8))
* **property-descriptor:** do not use document object in Safari web worker ([51f2e1f](https://github.com/angular/zone.js/commit/51f2e1f))
* Add WebSocket to the NO_EVENT_TARGET list to be patched as well ([#493](https://github.com/angular/zone.js/issues/493)) ([d8c15eb](https://github.com/angular/zone.js/commit/d8c15eb))
* fix wrong usage of == caught by closure compiler ([#510](https://github.com/angular/zone.js/issues/510)) ([d7d8eb5](https://github.com/angular/zone.js/commit/d7d8eb5))
* fluent interface for EventEmitter ([#475](https://github.com/angular/zone.js/issues/475)) ([c5130a6](https://github.com/angular/zone.js/commit/c5130a6))
* lint errors ([ed87c26](https://github.com/angular/zone.js/commit/ed87c26))
* make fetch promise patching safe ([16be7f9](https://github.com/angular/zone.js/commit/16be7f9)), closes [#451](https://github.com/angular/zone.js/issues/451)
* Make the check for ZoneAwarePromise more stringent ([#495](https://github.com/angular/zone.js/issues/495)) ([c69df25](https://github.com/angular/zone.js/commit/c69df25))
* run all timers in passage of time in a single fakeAsync's tick call ([a85db4c](https://github.com/angular/zone.js/commit/a85db4c)), closes [#454](https://github.com/angular/zone.js/issues/454)
* stop using class extends as it breaks rollup ([b52cf02](https://github.com/angular/zone.js/commit/b52cf02))
* use strict equality in scheduleQueueDrain ([#504](https://github.com/angular/zone.js/issues/504)) ([4b4249c](https://github.com/angular/zone.js/commit/4b4249c))
### Features
* add mocha support ([41a9047](https://github.com/angular/zone.js/commit/41a9047))
* **Error:** Rewrite Error stack frames to include zone ([e1c2a02](https://github.com/angular/zone.js/commit/e1c2a02))
<a name="0.6.25"></a>
## [0.6.25](https://github.com/angular/zone.js/compare/0.6.24...0.6.25) (2016-09-20)
### Bug Fixes
* **zonespecs:** revert unwrapping of zonespecs which actually require global ([#460](https://github.com/angular/zone.js/issues/460)) ([28a14f8](https://github.com/angular/zone.js/commit/28a14f8))
<a name="0.6.24"></a>
## [0.6.24](https://github.com/angular/zone.js/compare/v0.6.23...0.6.24) (2016-09-19)
### Bug Fixes
* **bundling:** switch to using umd bundles ([#457](https://github.com/angular/zone.js/issues/457)) ([8dd06e5](https://github.com/angular/zone.js/commit/8dd06e5)), closes [#456](https://github.com/angular/zone.js/issues/456)
<a name="0.6.23"></a>
## [0.6.23](https://github.com/angular/zone.js/compare/v0.6.22...v0.6.23) (2016-09-14)
### Bug Fixes
* **fetch:** correct chrome not able to load about://blank ([3844435](https://github.com/angular/zone.js/commit/3844435)), closes [#444](https://github.com/angular/zone.js/issues/444)
<a name="0.6.22"></a>
## [0.6.22](https://github.com/angular/zone.js/compare/v0.6.21...v0.6.22) (2016-09-14)
### Bug Fixes
* use fetch(about://blank) to prevent exception on MS Edge ([#442](https://github.com/angular/zone.js/issues/442)) ([8b81537](https://github.com/angular/zone.js/commit/8b81537)), closes [#436](https://github.com/angular/zone.js/issues/436) [#439](https://github.com/angular/zone.js/issues/439)
### Features
* **node:** patch most fs methods ([#438](https://github.com/angular/zone.js/issues/438)) ([4c8a155](https://github.com/angular/zone.js/commit/4c8a155))
* **node:** patch outgoing http requests to capture the zone ([#430](https://github.com/angular/zone.js/issues/430)) ([100b82b](https://github.com/angular/zone.js/commit/100b82b))
<a name="0.6.21"></a>
## [0.6.21](https://github.com/angular/zone.js/compare/v0.6.20...v0.6.21) (2016-09-11)
### Bug Fixes
* proper detection of global in WebWorker ([0a7a155](https://github.com/angular/zone.js/commit/0a7a155))
<a name="0.6.20"></a>
## [0.6.20](https://github.com/angular/zone.js/compare/v0.6.19...v0.6.20) (2016-09-10)
<a name="0.6.19"></a>
## [0.6.19](https://github.com/angular/zone.js/compare/v0.6.17...v0.6.19) (2016-09-10)
### Bug Fixes
* provide a more usefull error when configuring properties ([1fe4df0](https://github.com/angular/zone.js/commit/1fe4df0))
* **jasmine:** propagate all arguments of it/describe/etc... ([a85fd68](https://github.com/angular/zone.js/commit/a85fd68))
* **long-stack:** Safer writing of stack traces. ([6767ff5](https://github.com/angular/zone.js/commit/6767ff5))
* **promise:** support more aggressive optimization. ([#431](https://github.com/angular/zone.js/issues/431)) ([26fc3da](https://github.com/angular/zone.js/commit/26fc3da))
* **XHR:** Don't send sync XHR through ZONE ([6e2f13c](https://github.com/angular/zone.js/commit/6e2f13c)), closes [#377](https://github.com/angular/zone.js/issues/377)
### Features
* assert that right ZoneAwarePromise is available ([#420](https://github.com/angular/zone.js/issues/420)) ([4c35e5b](https://github.com/angular/zone.js/commit/4c35e5b))
<a name="0.6.17"></a>
## [0.6.17](https://github.com/angular/zone.js/compare/v0.6.15...v0.6.17) (2016-08-22)
### Bug Fixes
* **browser:** use XMLHttpRequest.DONE constant on target instead of the global interface ([#395](https://github.com/angular/zone.js/issues/395)) ([3b4c20b](https://github.com/angular/zone.js/commit/3b4c20b)), closes [#394](https://github.com/angular/zone.js/issues/394)
* **jasmine:** spelling error of 'describe' in jasmine patch prevented application of sync zone ([d38ccde](https://github.com/angular/zone.js/commit/d38ccde)), closes [#412](https://github.com/angular/zone.js/issues/412)
* **patchProperty:** return null as the default value ([#413](https://github.com/angular/zone.js/issues/413)) ([396942b](https://github.com/angular/zone.js/commit/396942b)), closes [#319](https://github.com/angular/zone.js/issues/319)
* IE10/11 timeout issues. ([382182c](https://github.com/angular/zone.js/commit/382182c))
<a name="0.6.15"></a>
## [0.6.15](https://github.com/angular/zone.js/compare/v0.6.14...v0.6.15) (2016-08-19)
### Bug Fixes
* broken build. ([#406](https://github.com/angular/zone.js/issues/406)) ([5e3c207](https://github.com/angular/zone.js/commit/5e3c207))
* **tasks:** do not drain the microtask queue early. ([ff88bb4](https://github.com/angular/zone.js/commit/ff88bb4))
* **tasks:** do not drain the microtask queue early. ([d4a1436](https://github.com/angular/zone.js/commit/d4a1436))
<a name="0.6.14"></a>
## [0.6.14](https://github.com/angular/zone.js/compare/v0.6.13...v0.6.14) (2016-08-17)
### Features
* **jasmine:** patch jasmine to understand zones. ([3a054be](https://github.com/angular/zone.js/commit/3a054be))
* **trackingZone:** Keep track of tasks to see outstanding tasks. ([4942b4a](https://github.com/angular/zone.js/commit/4942b4a))
<a name="0.6.13"></a>
## [0.6.13](https://github.com/angular/zone.js/compare/v0.6.12...v0.6.13) (2016-08-15)
### Bug Fixes
* **browser:** make Object.defineProperty patch safer ([#392](https://github.com/angular/zone.js/issues/392)) ([597c634](https://github.com/angular/zone.js/commit/597c634)), closes [#391](https://github.com/angular/zone.js/issues/391)
* **browser:** patch Window when EventTarget is missing. ([#368](https://github.com/angular/zone.js/issues/368)) ([fcef80d](https://github.com/angular/zone.js/commit/fcef80d)), closes [#367](https://github.com/angular/zone.js/issues/367)
* **browser:** patchTimer cancelAnimationFrame ([#353](https://github.com/angular/zone.js/issues/353)) ([bf77fbb](https://github.com/angular/zone.js/commit/bf77fbb)), closes [#326](https://github.com/angular/zone.js/issues/326) [Leaflet/Leaflet#4588](https://github.com/Leaflet/Leaflet/issues/4588)
* **browser:** should not throw with frozen prototypes ([#351](https://github.com/angular/zone.js/issues/351)) ([27ca2a9](https://github.com/angular/zone.js/commit/27ca2a9))
* **build:** fix broken master due to setTimeout not returning a number on node ([d43b4b8](https://github.com/angular/zone.js/commit/d43b4b8))
* **doc:** Fixed the home page example. ([#348](https://github.com/angular/zone.js/issues/348)) ([9a0aa4a](https://github.com/angular/zone.js/commit/9a0aa4a))
* throw if trying to load zone more then once. ([6df5f93](https://github.com/angular/zone.js/commit/6df5f93))
* **fakeAsync:** throw error on rejected promisees. ([fd1dfcc](https://github.com/angular/zone.js/commit/fd1dfcc))
* **promise:** allow Promise subclassing ([dafad98](https://github.com/angular/zone.js/commit/dafad98))
* **XHR.responseBlob:** don't access XHR.responseBlob on old android webkit ([#329](https://github.com/angular/zone.js/issues/329)) ([ed69756](https://github.com/angular/zone.js/commit/ed69756))
### Features
* return timeout Id in ZoneTask.toString (fixes [#341](https://github.com/angular/zone.js/issues/341)) ([80ae6a8](https://github.com/angular/zone.js/commit/80ae6a8)), closes [#375](https://github.com/angular/zone.js/issues/375)
* **jasmine:** Switch jasmine patch to use microtask and preserve zone. ([5f519de](https://github.com/angular/zone.js/commit/5f519de))
* **ProxySpec:** create a ProxySpec which can proxy to other ZoneSpecs. ([2d02e39](https://github.com/angular/zone.js/commit/2d02e39))
* **zone:** Add Zone.getZone api ([0621014](https://github.com/angular/zone.js/commit/0621014))
<a name="0.6.12"></a>
## [0.6.12](https://github.com/angular/zone.js/compare/v0.6.11...v0.6.12) (2016-04-19)
### Bug Fixes
* **property-descriptor:** do not fail for events without targets ([3a8deef](https://github.com/angular/zone.js/commit/3a8deef))
### Features
* Add a zone spec for fake async test zone. ([#330](https://github.com/angular/zone.js/issues/330)) ([34159b4](https://github.com/angular/zone.js/commit/34159b4))
<a name="0.6.11"></a>
## [0.6.11](https://github.com/angular/zone.js/compare/v0.6.9...v0.6.11) (2016-04-14)
### Bug Fixes
* Suppress closure compiler warnings about unknown 'process' variable. ([e125173](https://github.com/angular/zone.js/commit/e125173)), closes [#295](https://github.com/angular/zone.js/issues/295)
* **setTimeout:** fix for [#290](https://github.com/angular/zone.js/issues/290), allow clearTimeout to be called in setTimeout callback ([a6967ad](https://github.com/angular/zone.js/commit/a6967ad)), closes [#301](https://github.com/angular/zone.js/issues/301)
* **WebSocket patch:** fix WebSocket constants copy ([#299](https://github.com/angular/zone.js/issues/299)) ([5dc4339](https://github.com/angular/zone.js/commit/5dc4339))
* **xhr:** XHR macrotasks allow abort after XHR has completed ([#311](https://github.com/angular/zone.js/issues/311)) ([c70f011](https://github.com/angular/zone.js/commit/c70f011))
* **zone:** remove debugger statement ([#292](https://github.com/angular/zone.js/issues/292)) ([01cec16](https://github.com/angular/zone.js/commit/01cec16))
* window undefined in node environments ([f8d5dc7](https://github.com/angular/zone.js/commit/f8d5dc7)), closes [#305](https://github.com/angular/zone.js/issues/305)
### Features
* **zonespec:** add a spec for synchronous tests ([#294](https://github.com/angular/zone.js/issues/294)) ([55da3d8](https://github.com/angular/zone.js/commit/55da3d8))
* node/node ([29fc5d2](https://github.com/angular/zone.js/commit/29fc5d2))
<a name="0.6.9"></a>
## [0.6.9](https://github.com/angular/zone.js/compare/v0.6.5...v0.6.9) (2016-04-04)
### Bug Fixes
* Allow calling clearTimeout from within the setTimeout callback ([a8ea55d](https://github.com/angular/zone.js/commit/a8ea55d)), closes [#302](https://github.com/angular/zone.js/issues/302)
* Canceling already run task should not double decrement task counter ([faa3485](https://github.com/angular/zone.js/commit/faa3485)), closes [#290](https://github.com/angular/zone.js/issues/290)
* **xhr:** don't throw on an xhr which is aborted before sending ([8827e1e](https://github.com/angular/zone.js/commit/8827e1e))
* **zone:** remove debugger statement ([d7c116b](https://github.com/angular/zone.js/commit/d7c116b))
### Features
* **zonespec:** add a spec for synchronous tests ([0a6a434](https://github.com/angular/zone.js/commit/0a6a434))
* treat XHRs as macrotasks ([fd39f97](https://github.com/angular/zone.js/commit/fd39f97))
<a name="0.6.5"></a>
## [0.6.5](https://github.com/angular/zone.js/compare/v0.6.2...v0.6.5) (2016-03-21)
### Bug Fixes
* disable safari 7 ([4a4d4f6](https://github.com/angular/zone.js/commit/4a4d4f6))
* **browser/utils:** calling removeEventListener twice with the same args should not cause errors ([1787339](https://github.com/angular/zone.js/commit/1787339)), closes [#283](https://github.com/angular/zone.js/issues/283) [#284](https://github.com/angular/zone.js/issues/284)
* **patching:** call native cancel method ([5783663](https://github.com/angular/zone.js/commit/5783663)), closes [#278](https://github.com/angular/zone.js/issues/278) [#279](https://github.com/angular/zone.js/issues/279)
* **utils:** add the ability to prevent the default action of onEvent (onclick, onpaste,etc..) by returning false. ([99940c3](https://github.com/angular/zone.js/commit/99940c3)), closes [#236](https://github.com/angular/zone.js/issues/236)
* **WebSocket patch:** keep WebSocket constants ([f25b087](https://github.com/angular/zone.js/commit/f25b087)), closes [#267](https://github.com/angular/zone.js/issues/267)
* **zonespec:** Do not crash on error if last task had no data ([0dba019](https://github.com/angular/zone.js/commit/0dba019)), closes [#281](https://github.com/angular/zone.js/issues/281)
### Features
* **indexdb:** Added property patches and event target methods as well as tests for Indexed DB ([84a251f](https://github.com/angular/zone.js/commit/84a251f)), closes [#204](https://github.com/angular/zone.js/issues/204)
* **zonespec:** add a spec for asynchronous tests ([aeeb05c](https://github.com/angular/zone.js/commit/aeeb05c)), closes [#275](https://github.com/angular/zone.js/issues/275)
<a name="0.6.2"></a>
## [0.6.2](https://github.com/angular/zone.js/compare/v0.6.1...v0.6.2) (2016-03-03)
<a name="0.6.1"></a>
## [0.6.1](https://github.com/angular/zone.js/compare/v0.6.0...v0.6.1) (2016-02-29)
<a name="0.6.0"></a>
# [0.6.0](https://github.com/angular/zone.js/compare/v0.5.15...v0.6.0) (2016-02-29)
### Chores
* **everything:** Major Zone Rewrite/Reimplementation ([63d4552](https://github.com/angular/zone.js/commit/63d4552))
### BREAKING CHANGES
* everything: This is a brand new implementation which is not backwards compatible.
<a name="0.5.15"></a>
## [0.5.15](https://github.com/angular/zone.js/compare/v0.5.14...v0.5.15) (2016-02-17)
### Bug Fixes
* **WebWorker:** Patch WebSockets and XMLHttpRequest in WebWorker ([45a6bc1](https://github.com/angular/zone.js/commit/45a6bc1)), closes [#249](https://github.com/angular/zone.js/issues/249)
* **WebWorker:** Patch WebSockets and XMLHttpRequest in WebWorker ([9041a3a](https://github.com/angular/zone.js/commit/9041a3a)), closes [#249](https://github.com/angular/zone.js/issues/249)
<a name="0.5.14"></a>
## [0.5.14](https://github.com/angular/zone.js/compare/v0.5.11...v0.5.14) (2016-02-11)
<a name="0.5.11"></a>
## [0.5.11](https://github.com/angular/zone.js/compare/v0.5.10...v0.5.11) (2016-01-27)
### Bug Fixes
* correct incorrect example path in karma config ([b0a624d](https://github.com/angular/zone.js/commit/b0a624d))
* correct test relaying on jasmine timeout ([4f7d6ae](https://github.com/angular/zone.js/commit/4f7d6ae))
* **WebSocket:** don't patch EventTarget methods twice ([345e56c](https://github.com/angular/zone.js/commit/345e56c)), closes [#235](https://github.com/angular/zone.js/issues/235)
### Features
* **wtf:** add wtf support to (set/clear)Timeout/Interval/Immediate ([6659fd5](https://github.com/angular/zone.js/commit/6659fd5))
<a name="0.5.10"></a>
## [0.5.10](https://github.com/angular/zone.js/compare/v0.5.9...v0.5.10) (2015-12-11)
### Bug Fixes
* **keys:** Do not use Symbol which are broken in Chrome 39.0.2171 (Dartium) ([c48301b](https://github.com/angular/zone.js/commit/c48301b))
* **Promise:** Make sure we check for native Promise before es6-promise gets a chance to polyfill ([fa18d4c](https://github.com/angular/zone.js/commit/fa18d4c))
<a name="0.5.9"></a>
## [0.5.9](https://github.com/angular/zone.js/compare/v0.5.8...v0.5.9) (2015-12-09)
### Bug Fixes
* **keys:** do not declare functions inside blocks ([d44d699](https://github.com/angular/zone.js/commit/d44d699)), closes [#194](https://github.com/angular/zone.js/issues/194)
* **keys:** Symbol is being checked for type of function ([6714be6](https://github.com/angular/zone.js/commit/6714be6))
* **mutation-observe:** output of typeof operator should be string ([19703e3](https://github.com/angular/zone.js/commit/19703e3))
* **util:** origin addEventListener/removeEventListener should be called without eventListener ([26e7f51](https://github.com/angular/zone.js/commit/26e7f51)), closes [#198](https://github.com/angular/zone.js/issues/198)
* **utils:** should have no effect when called addEventListener/removeEventListener without eventListener. ([5bcc6ae](https://github.com/angular/zone.js/commit/5bcc6ae))
<a name="0.5.8"></a>
## [0.5.8](https://github.com/angular/zone.js/compare/v0.5.7...v0.5.8) (2015-10-06)
### Bug Fixes
* **addEventListener:** when called from the global scope ([a23d61a](https://github.com/angular/zone.js/commit/a23d61a)), closes [#190](https://github.com/angular/zone.js/issues/190)
* **EventTarget:** apply the patch even if `Window` is not defined ([32c6df9](https://github.com/angular/zone.js/commit/32c6df9))
<a name="0.5.7"></a>
## [0.5.7](https://github.com/angular/zone.js/compare/v0.5.6...v0.5.7) (2015-09-29)
### Bug Fixes
* **RequestAnimationFrame:** pass the timestamp to the callback ([79a37c0](https://github.com/angular/zone.js/commit/79a37c0)), closes [#187](https://github.com/angular/zone.js/issues/187)
<a name="0.5.6"></a>
## [0.5.6](https://github.com/angular/zone.js/compare/v0.5.5...v0.5.6) (2015-09-25)
### Bug Fixes
* **Jasmine:** add support for jasmine 2 done.fail() ([1d4370b](https://github.com/angular/zone.js/commit/1d4370b)), closes [#180](https://github.com/angular/zone.js/issues/180)
* **utils:** fixes event target patch in web workers ([ad5c0c8](https://github.com/angular/zone.js/commit/ad5c0c8))
<a name="0.5.5"></a>
## [0.5.5](https://github.com/angular/zone.js/compare/v0.5.4...v0.5.5) (2015-09-11)
### Bug Fixes
* **lib/utils:** adds compliant handling of useCapturing param for EventTarget methods ([dd2e1bf](https://github.com/angular/zone.js/commit/dd2e1bf))
* **lib/utils:** fixes incorrect behaviour when re-adding the same event listener fn ([1b804cf](https://github.com/angular/zone.js/commit/1b804cf))
* **longStackTraceZone:** modifies stackFramesFilter to exclude zone.js frames ([50ce9f3](https://github.com/angular/zone.js/commit/50ce9f3))
### Features
* **lib/core:** add/removeEventListener hooks ([1897440](https://github.com/angular/zone.js/commit/1897440))
* **lib/patch/file-reader:** zone-binds FileReader#onEventName listeners ([ce589b9](https://github.com/angular/zone.js/commit/ce589b9)), closes [#137](https://github.com/angular/zone.js/issues/137)
<a name="0.5.4"></a>
## [0.5.4](https://github.com/angular/zone.js/compare/v0.5.3...v0.5.4) (2015-08-31)
### Bug Fixes
* js path in examples ([c7a2ed9](https://github.com/angular/zone.js/commit/c7a2ed9))
* **zone:** fix conflict with Polymer elements ([77b4c0d](https://github.com/angular/zone.js/commit/77b4c0d))
### Features
* **patch:** support requestAnimationFrame time loops ([3d6dc08](https://github.com/angular/zone.js/commit/3d6dc08))
<a name="0.5.3"></a>
## [0.5.3](https://github.com/angular/zone.js/compare/v0.5.2...v0.5.3) (2015-08-21)
### Bug Fixes
* **addEventListener patch:** ignore FunctionWrapper for IE11 & Edge dev tools ([3b0ca3f](https://github.com/angular/zone.js/commit/3b0ca3f))
* **utils:** event listener patches break when passed an object implementing EventListener ([af88ff8](https://github.com/angular/zone.js/commit/af88ff8))
* **WebWorker:** Fix patching in WebWorker ([2cc59d8](https://github.com/angular/zone.js/commit/2cc59d8))
### Features
* **zone.js:** support Android browser ([93b5555](https://github.com/angular/zone.js/commit/93b5555))
<a name="0.5.2"></a>
## [0.5.2](https://github.com/angular/zone.js/compare/v0.5.1...v0.5.2) (2015-07-01)
### Bug Fixes
* **jasmine patch:** forward timeout ([2dde717](https://github.com/angular/zone.js/commit/2dde717))
* **zone.bind:** throw an error if arg is not a function ([ee4262a](https://github.com/angular/zone.js/commit/ee4262a))
<a name="0.5.1"></a>
## [0.5.1](https://github.com/angular/zone.js/compare/v0.5.0...v0.5.1) (2015-06-10)
### Bug Fixes
* **PatchClass:** copy static properties ([b91f8fe](https://github.com/angular/zone.js/commit/b91f8fe)), closes [#127](https://github.com/angular/zone.js/issues/127)
* **register-element:** add check for callback being own property of opts ([8bce00e](https://github.com/angular/zone.js/commit/8bce00e)), closes [#52](https://github.com/angular/zone.js/issues/52)
### Features
* **fetch:** patch the fetch API ([4d3d524](https://github.com/angular/zone.js/commit/4d3d524)), closes [#108](https://github.com/angular/zone.js/issues/108)
* **geolocation:** patch the API ([cd13da1](https://github.com/angular/zone.js/commit/cd13da1)), closes [#113](https://github.com/angular/zone.js/issues/113)
* **jasmine:** export the jasmine patch ([639d5e7](https://github.com/angular/zone.js/commit/639d5e7))
* **test:** serve lib/ files instead of dist/ ([f835213](https://github.com/angular/zone.js/commit/f835213))
* **zone.js:** support IE9+ ([554fae0](https://github.com/angular/zone.js/commit/554fae0))
<a name="0.5.0"></a>
# [0.5.0](https://github.com/angular/zone.js/compare/v0.4.4...v0.5.0) (2015-05-08)
### Bug Fixes
* always run jasmine's done callbacks for async tests in jasmine's zone ([b7f3d04](https://github.com/angular/zone.js/commit/b7f3d04)), closes [#91](https://github.com/angular/zone.js/issues/91)
* don't fork new zones for callbacks from the root zone ([531d0ec](https://github.com/angular/zone.js/commit/531d0ec)), closes [#92](https://github.com/angular/zone.js/issues/92)
* **MutationObserver:** executes hooks in the creation zone ([3122a48](https://github.com/angular/zone.js/commit/3122a48))
* **test:** fix an ineffective assertion ([d85d2cf](https://github.com/angular/zone.js/commit/d85d2cf))
* minor fixes ([18f5511](https://github.com/angular/zone.js/commit/18f5511))
### Code Refactoring
* split zone.js into CJS modules, add zone-microtask.js ([2e52900](https://github.com/angular/zone.js/commit/2e52900))
### Features
* **scheduling:** Prefer MutationObserver over Promise in FF ([038bdd9](https://github.com/angular/zone.js/commit/038bdd9))
* **scheduling:** Support Promise.then() fallbacks to enqueue a microtask ([74eff1c](https://github.com/angular/zone.js/commit/74eff1c))
* add isRootZone api ([bf925bf](https://github.com/angular/zone.js/commit/bf925bf))
* make root zone id to be 1 ([605e213](https://github.com/angular/zone.js/commit/605e213))
### BREAKING CHANGES
* New child zones are now created only from a async task
that installed a custom zone.
Previously even without a custom zone installed (e.g.
LongStacktracesZone), we would spawn new
child zones for all asynchronous events. This is undesirable and
generally not useful.
It does not make sense for us to create new zones for callbacks from the
root zone since we care
only about callbacks from installed custom zones. This reduces the
overhead of zones.
This primarily means that LongStackTraces zone won't be able to trace
events back to Zone.init(),
but instead the starting point will be the installation of the
LongStacktracesZone. In all practical
situations this should be sufficient.
* zone.js as well as *-zone.js files are moved from / to dist/
<a name="0.4.4"></a>
## [0.4.4](https://github.com/angular/zone.js/compare/v0.4.3...v0.4.4) (2015-05-07)
### Bug Fixes
* commonjs wrapper ([7b4fdde](https://github.com/angular/zone.js/commit/7b4fdde)), closes [#19](https://github.com/angular/zone.js/issues/19)
* fork the zone in first example (README) ([7b6e8ed](https://github.com/angular/zone.js/commit/7b6e8ed))
* prevent aliasing original window reference ([63b42bd](https://github.com/angular/zone.js/commit/63b42bd))
* use strcit mode for the zone.js code only ([16855e5](https://github.com/angular/zone.js/commit/16855e5))
* **test:** use console.log rather than dump in tests ([490e6dd](https://github.com/angular/zone.js/commit/490e6dd))
* **websockets:** patch websockets via descriptors ([d725f46](https://github.com/angular/zone.js/commit/d725f46)), closes [#81](https://github.com/angular/zone.js/issues/81)
* **websockets:** properly patch websockets in Safari 7.0 ([3ba6fa1](https://github.com/angular/zone.js/commit/3ba6fa1)), closes [#88](https://github.com/angular/zone.js/issues/88)
* **websockets:** properly patch websockets on Safari 7.1 ([1799a20](https://github.com/angular/zone.js/commit/1799a20))
### Features
* add websockets example ([edb17d2](https://github.com/angular/zone.js/commit/edb17d2))
* log a warning if we suspect duplicate Zone install ([657f6fe](https://github.com/angular/zone.js/commit/657f6fe))
<a name="0.4.3"></a>
## [0.4.3](https://github.com/angular/zone.js/compare/v0.4.2...v0.4.3) (2015-04-08)
### Bug Fixes
* **zone:** keep argument[0] refs around. ([48573ff](https://github.com/angular/zone.js/commit/48573ff))
<a name="0.4.2"></a>
## [0.4.2](https://github.com/angular/zone.js/compare/v0.4.1...v0.4.2) (2015-03-27)
### Bug Fixes
* **zone.js:** don't make function declaration in block scope ([229fd8f](https://github.com/angular/zone.js/commit/229fd8f)), closes [#53](https://github.com/angular/zone.js/issues/53) [#54](https://github.com/angular/zone.js/issues/54)
### Features
* **bindPromiseFn:** add bindPromiseFn method ([643f2ac](https://github.com/angular/zone.js/commit/643f2ac)), closes [#49](https://github.com/angular/zone.js/issues/49)
* **lstz:** allow getLongStacktrace to be called with zero args ([26a4dc2](https://github.com/angular/zone.js/commit/26a4dc2)), closes [#47](https://github.com/angular/zone.js/issues/47)
* **Zone:** add unique id to each zone ([fb338b6](https://github.com/angular/zone.js/commit/fb338b6)), closes [#45](https://github.com/angular/zone.js/issues/45)
<a name="0.4.1"></a>
## [0.4.1](https://github.com/angular/zone.js/compare/v0.4.0...v0.4.1) (2015-02-20)
### Bug Fixes
* **patchViaPropertyDescriptor:** disable if properties are not configurable ([fb5e644](https://github.com/angular/zone.js/commit/fb5e644)), closes [#42](https://github.com/angular/zone.js/issues/42)
<a name="0.4.0"></a>
# [0.4.0](https://github.com/angular/zone.js/compare/v0.3.0...v0.4.0) (2015-02-04)
### Bug Fixes
* **WebSocket:** patch WebSocket instance ([7b8e1e6](https://github.com/angular/zone.js/commit/7b8e1e6))
<a name="0.3.0"></a>
# [0.3.0](https://github.com/angular/zone.js/compare/v0.2.4...v0.3.0) (2014-06-12)
### Bug Fixes
* add events for webgl contexts ([4b6e411](https://github.com/angular/zone.js/commit/4b6e411))
* bind prototype chain callback of custom element descriptor ([136e518](https://github.com/angular/zone.js/commit/136e518))
* dequeue tasks from the zone that enqueued it ([f127fd4](https://github.com/angular/zone.js/commit/f127fd4))
* do not reconfig property descriptors of prototypes ([e9dfbed](https://github.com/angular/zone.js/commit/e9dfbed))
* patch property descriptors in Object.create ([7b7258b](https://github.com/angular/zone.js/commit/7b7258b)), closes [#24](https://github.com/angular/zone.js/issues/24)
* support mozRequestAnimationFrame ([886f67d](https://github.com/angular/zone.js/commit/886f67d))
* wrap non-configurable custom element callbacks ([383b479](https://github.com/angular/zone.js/commit/383b479)), closes [#24](https://github.com/angular/zone.js/issues/24)
* wrap Object.defineProperties ([f587f17](https://github.com/angular/zone.js/commit/f587f17)), closes [#24](https://github.com/angular/zone.js/issues/24)
<a name="0.2.4"></a>
## [0.2.4](https://github.com/angular/zone.js/compare/v0.2.3...v0.2.4) (2014-05-23)
<a name="0.2.3"></a>
## [0.2.3](https://github.com/angular/zone.js/compare/v0.2.2...v0.2.3) (2014-05-23)
### Bug Fixes
* remove dump ([45fb7ba](https://github.com/angular/zone.js/commit/45fb7ba))
<a name="0.2.2"></a>
## [0.2.2](https://github.com/angular/zone.js/compare/v0.2.1...v0.2.2) (2014-05-22)
### Bug Fixes
* correctly detect support for document.registerElement ([ab1d487](https://github.com/angular/zone.js/commit/ab1d487))
* dont automagically dequeue on setInterval ([da99e15](https://github.com/angular/zone.js/commit/da99e15))
* fork should deep clone objects ([21b47ae](https://github.com/angular/zone.js/commit/21b47ae))
* support MutationObserver.disconnect ([ad711b8](https://github.com/angular/zone.js/commit/ad711b8))
### Features
* add stackFramesFilter to longStackTraceZone ([7133de0](https://github.com/angular/zone.js/commit/7133de0))
* expose hooks for enqueuing and dequing tasks ([ba72f34](https://github.com/angular/zone.js/commit/ba72f34))
* improve countingZone and example ([86328fb](https://github.com/angular/zone.js/commit/86328fb))
* support document.registerElement ([d3c785a](https://github.com/angular/zone.js/commit/d3c785a)), closes [#18](https://github.com/angular/zone.js/issues/18)
<a name="0.2.1"></a>
## [0.2.1](https://github.com/angular/zone.js/compare/v0.2.0...v0.2.1) (2014-04-24)
### Bug Fixes
* add support for WebKitMutationObserver ([d1a2c8e](https://github.com/angular/zone.js/commit/d1a2c8e))
* preserve setters when wrapping XMLHttpRequest ([fb46688](https://github.com/angular/zone.js/commit/fb46688)), closes [#17](https://github.com/angular/zone.js/issues/17)
<a name="0.2.0"></a>
# [0.2.0](https://github.com/angular/zone.js/compare/v0.1.1...v0.2.0) (2014-04-17)
### Bug Fixes
* patch all properties on the proto chain ([b6d76f0](https://github.com/angular/zone.js/commit/b6d76f0))
* patch MutationObserver ([1c4e85e](https://github.com/angular/zone.js/commit/1c4e85e))
* wrap XMLHttpRequest when we cant patch protos ([76de58e](https://github.com/angular/zone.js/commit/76de58e))
### Features
* add exceptZone ([b134391](https://github.com/angular/zone.js/commit/b134391))
<a name="0.1.1"></a>
## [0.1.1](https://github.com/angular/zone.js/compare/v0.1.0...v0.1.1) (2014-03-31)
### Features
* add commonjs support ([0fe349e](https://github.com/angular/zone.js/commit/0fe349e))
<a name="0.1.0"></a>
# [0.1.0](https://github.com/angular/zone.js/compare/v0.0.0...v0.1.0) (2014-03-31)
### Bug Fixes
* improve patching browsers with EventTarget ([7d3a8b1](https://github.com/angular/zone.js/commit/7d3a8b1))
* improve stacktrace capture on Safari ([46a6fbc](https://github.com/angular/zone.js/commit/46a6fbc))
* long stack trace test ([01ce3b3](https://github.com/angular/zone.js/commit/01ce3b3))
* prevent calling addEventListener on non-functions ([7acebca](https://github.com/angular/zone.js/commit/7acebca))
* throw if a zone does not define an onError hook ([81d5f49](https://github.com/angular/zone.js/commit/81d5f49))
* throw if a zone does not define an onError hook ([3485c1b](https://github.com/angular/zone.js/commit/3485c1b))
### Features
* add decorator syntax ([c6202a1](https://github.com/angular/zone.js/commit/c6202a1))
* add onZoneCreated hook ([f7badb6](https://github.com/angular/zone.js/commit/f7badb6))
* patch onclick in Chrome and Safari ([7205295](https://github.com/angular/zone.js/commit/7205295))
* refactor and test counting zone ([648a95d](https://github.com/angular/zone.js/commit/648a95d))
* support Promise ([091f44e](https://github.com/angular/zone.js/commit/091f44e))
<a name="0.0.0"></a>
# 0.0.0 (2013-09-18)

View File

@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2016 Google, Inc.
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.

View File

@@ -0,0 +1,25 @@
/**
@license
The MIT License
Copyright (c) 2016 Google, Inc.
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.
*/

View File

@@ -0,0 +1,39 @@
# Zone.js
[![Build Status](https://travis-ci.org/angular/zone.js.png)](https://travis-ci.org/angular/zone.js)
[![CDNJS](https://img.shields.io/cdnjs/v/zone.js.svg)](https://cdnjs.com/libraries/zone.js)
Implements _Zones_ for JavaScript, inspired by [Dart](https://www.dartlang.org/articles/zones/).
> If you're using zone.js via unpkg please provide a query param `?main=browser`
`https://unpkg.com/zone.js?main=browser`
> If you're using the following library, make sure you import them first
> * 'newrelic' as it patches global.Promise before zone,js does
> * 'async-listener' as it patches global.setTimeout, global.setInterval before zone,js does
> * 'continuation-local-storage' as it uses async-listener
# NEW Zone.js POST-v0.6.0
See the new API [here](./dist/zone.js.d.ts).
Read up on [Zone Primer](https://docs.google.com/document/d/1F5Ug0jcrm031vhSMJEOgp1l-Is-Vf0UCNDY-LsQtAIY).
## What's a Zone?
A Zone is an execution context that persists across async tasks.
You can think of it as [thread-local storage](http://en.wikipedia.org/wiki/Thread-local_storage) for JavaScript VMs.
See this video from ng-conf 2014 for a detailed explanation:
[![screenshot of the zone.js presentation and ng-conf 2014](/presentation.png)](//www.youtube.com/watch?v=3IqtmUscE_U)
## See also
* [async-listener](https://github.com/othiym23/async-listener) - a similar library for node
* [Async stack traces in Chrome](http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/)
* [strongloop/zone](https://github.com/strongloop/zone)
* [vizone](https://github.com/gilbox/vizone) - control flow visualizer that uses zone.js
## License
MIT

View File

@@ -0,0 +1,88 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
(factory());
}(this, (function () { 'use strict';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
var AsyncTestZoneSpec = (function () {
function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) {
this._pendingMicroTasks = false;
this._pendingMacroTasks = false;
this._alreadyErrored = false;
this.runZone = Zone.current;
this._finishCallback = finishCallback;
this._failCallback = failCallback;
this.name = 'asyncTestZone for ' + namePrefix;
}
AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () {
var _this = this;
if (!(this._pendingMicroTasks || this._pendingMacroTasks)) {
// We do this because we would like to catch unhandled rejected promises.
this.runZone.run(function () {
setTimeout(function () {
if (!_this._alreadyErrored && !(_this._pendingMicroTasks || _this._pendingMacroTasks)) {
_this._finishCallback();
}
}, 0);
});
}
};
// Note - we need to use onInvoke at the moment to call finish when a test is
// fully synchronous. TODO(juliemr): remove this when the logic for
// onHasTask changes and it calls whenever the task queues are dirty.
AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) {
try {
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source);
}
finally {
this._finishCallbackIfDone();
}
};
AsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) {
// Let the parent try to handle the error.
var result = parentZoneDelegate.handleError(targetZone, error);
if (result) {
this._failCallback(error);
this._alreadyErrored = true;
}
return false;
};
AsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, currentZone, targetZone, task) {
if (task.type == 'macroTask' && task.source == 'setInterval') {
this._failCallback('Cannot use setInterval from within an async zone test.');
return;
}
return delegate.scheduleTask(targetZone, task);
};
AsyncTestZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) {
delegate.hasTask(target, hasTaskState);
if (hasTaskState.change == 'microTask') {
this._pendingMicroTasks = hasTaskState.microTask;
this._finishCallbackIfDone();
}
else if (hasTaskState.change == 'macroTask') {
this._pendingMacroTasks = hasTaskState.macroTask;
this._finishCallbackIfDone();
}
};
return AsyncTestZoneSpec;
}());
// Export the class so that new instances can be created with proper
// constructor params.
Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec;
})));

View File

@@ -0,0 +1,256 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
(factory());
}(this, (function () { 'use strict';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function (global) {
var Scheduler = (function () {
function Scheduler() {
// Next scheduler id.
this.nextId = 0;
// Scheduler queue with the tuple of end time and callback function - sorted by end time.
this._schedulerQueue = [];
// Current simulated time in millis.
this._currentTime = 0;
}
Scheduler.prototype.scheduleFunction = function (cb, delay, args, id) {
if (args === void 0) { args = []; }
if (id === void 0) { id = -1; }
var currentId = id < 0 ? this.nextId++ : id;
var endTime = this._currentTime + delay;
// Insert so that scheduler queue remains sorted by end time.
var newEntry = { endTime: endTime, id: currentId, func: cb, args: args, delay: delay };
var i = 0;
for (; i < this._schedulerQueue.length; i++) {
var currentEntry = this._schedulerQueue[i];
if (newEntry.endTime < currentEntry.endTime) {
break;
}
}
this._schedulerQueue.splice(i, 0, newEntry);
return currentId;
};
Scheduler.prototype.removeScheduledFunctionWithId = function (id) {
for (var i = 0; i < this._schedulerQueue.length; i++) {
if (this._schedulerQueue[i].id == id) {
this._schedulerQueue.splice(i, 1);
break;
}
}
};
Scheduler.prototype.tick = function (millis) {
if (millis === void 0) { millis = 0; }
var finalTime = this._currentTime + millis;
while (this._schedulerQueue.length > 0) {
var current = this._schedulerQueue[0];
if (finalTime < current.endTime) {
// Done processing the queue since it's sorted by endTime.
break;
}
else {
// Time to run scheduled function. Remove it from the head of queue.
var current_1 = this._schedulerQueue.shift();
this._currentTime = current_1.endTime;
var retval = current_1.func.apply(global, current_1.args);
if (!retval) {
// Uncaught exception in the current scheduled function. Stop processing the queue.
break;
}
}
}
this._currentTime = finalTime;
};
return Scheduler;
}());
var FakeAsyncTestZoneSpec = (function () {
function FakeAsyncTestZoneSpec(namePrefix) {
this._scheduler = new Scheduler();
this._microtasks = [];
this._lastError = null;
this._uncaughtPromiseErrors = Promise[Zone['__symbol__']('uncaughtPromiseErrors')];
this.pendingPeriodicTimers = [];
this.pendingTimers = [];
this.properties = { 'FakeAsyncTestZoneSpec': this };
this.name = 'fakeAsyncTestZone for ' + namePrefix;
}
FakeAsyncTestZoneSpec.assertInZone = function () {
if (Zone.current.get('FakeAsyncTestZoneSpec') == null) {
throw new Error('The code should be running in the fakeAsync zone to call this function');
}
};
FakeAsyncTestZoneSpec.prototype._fnAndFlush = function (fn, completers) {
var _this = this;
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
fn.apply(global, args);
if (_this._lastError === null) {
if (completers.onSuccess != null) {
completers.onSuccess.apply(global);
}
// Flush microtasks only on success.
_this.flushMicrotasks();
}
else {
if (completers.onError != null) {
completers.onError.apply(global);
}
}
// Return true if there were no errors, false otherwise.
return _this._lastError === null;
};
};
FakeAsyncTestZoneSpec._removeTimer = function (timers, id) {
var index = timers.indexOf(id);
if (index > -1) {
timers.splice(index, 1);
}
};
FakeAsyncTestZoneSpec.prototype._dequeueTimer = function (id) {
var _this = this;
return function () {
FakeAsyncTestZoneSpec._removeTimer(_this.pendingTimers, id);
};
};
FakeAsyncTestZoneSpec.prototype._requeuePeriodicTimer = function (fn, interval, args, id) {
var _this = this;
return function () {
// Requeue the timer callback if it's not been canceled.
if (_this.pendingPeriodicTimers.indexOf(id) !== -1) {
_this._scheduler.scheduleFunction(fn, interval, args, id);
}
};
};
FakeAsyncTestZoneSpec.prototype._dequeuePeriodicTimer = function (id) {
var _this = this;
return function () {
FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id);
};
};
FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args) {
var removeTimerFn = this._dequeueTimer(this._scheduler.nextId);
// Queue the callback and dequeue the timer on success and error.
var cb = this._fnAndFlush(fn, { onSuccess: removeTimerFn, onError: removeTimerFn });
var id = this._scheduler.scheduleFunction(cb, delay, args);
this.pendingTimers.push(id);
return id;
};
FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) {
FakeAsyncTestZoneSpec._removeTimer(this.pendingTimers, id);
this._scheduler.removeScheduledFunctionWithId(id);
};
FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval) {
var args = [];
for (var _i = 2; _i < arguments.length; _i++) {
args[_i - 2] = arguments[_i];
}
var id = this._scheduler.nextId;
var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) };
var cb = this._fnAndFlush(fn, completers);
// Use the callback created above to requeue on success.
completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id);
// Queue the callback and dequeue the periodic timer only on error.
this._scheduler.scheduleFunction(cb, interval, args);
this.pendingPeriodicTimers.push(id);
return id;
};
FakeAsyncTestZoneSpec.prototype._clearInterval = function (id) {
FakeAsyncTestZoneSpec._removeTimer(this.pendingPeriodicTimers, id);
this._scheduler.removeScheduledFunctionWithId(id);
};
FakeAsyncTestZoneSpec.prototype._resetLastErrorAndThrow = function () {
var error = this._lastError || this._uncaughtPromiseErrors[0];
this._uncaughtPromiseErrors.length = 0;
this._lastError = null;
throw error;
};
FakeAsyncTestZoneSpec.prototype.tick = function (millis) {
if (millis === void 0) { millis = 0; }
FakeAsyncTestZoneSpec.assertInZone();
this.flushMicrotasks();
this._scheduler.tick(millis);
if (this._lastError !== null) {
this._resetLastErrorAndThrow();
}
};
FakeAsyncTestZoneSpec.prototype.flushMicrotasks = function () {
var _this = this;
FakeAsyncTestZoneSpec.assertInZone();
var flushErrors = function () {
if (_this._lastError !== null || _this._uncaughtPromiseErrors.length) {
// If there is an error stop processing the microtask queue and rethrow the error.
_this._resetLastErrorAndThrow();
}
};
while (this._microtasks.length > 0) {
var microtask = this._microtasks.shift();
microtask();
}
flushErrors();
};
FakeAsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) {
switch (task.type) {
case 'microTask':
this._microtasks.push(task.invoke);
break;
case 'macroTask':
switch (task.source) {
case 'setTimeout':
task.data['handleId'] =
this._setTimeout(task.invoke, task.data['delay'], task.data['args']);
break;
case 'setInterval':
task.data['handleId'] =
this._setInterval(task.invoke, task.data['delay'], task.data['args']);
break;
case 'XMLHttpRequest.send':
throw new Error('Cannot make XHRs from within a fake async test.');
default:
task = delegate.scheduleTask(target, task);
}
break;
case 'eventTask':
task = delegate.scheduleTask(target, task);
break;
}
return task;
};
FakeAsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) {
switch (task.source) {
case 'setTimeout':
return this._clearTimeout(task.data['handleId']);
case 'setInterval':
return this._clearInterval(task.data['handleId']);
default:
return delegate.cancelTask(target, task);
}
};
FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) {
this._lastError = error;
return false; // Don't propagate error to parent zone.
};
return FakeAsyncTestZoneSpec;
}());
// Export the class so that new instances can be created with proper
// constructor params.
Zone['FakeAsyncTestZoneSpec'] = FakeAsyncTestZoneSpec;
})(typeof window === 'object' && window || typeof self === 'object' && self || global);
})));

View File

@@ -0,0 +1,139 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
(factory());
}(this, (function () { 'use strict';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function () {
var __extends = function (d, b) {
for (var p in b)
if (b.hasOwnProperty(p))
d[p] = b[p];
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
// Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs
// in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503)
if (!Zone)
throw new Error('Missing: zone.js');
if (typeof jasmine == 'undefined')
throw new Error('Missing: jasmine.js');
if (jasmine['__zone_patch__'])
throw new Error('\'jasmine\' has already been patched with \'Zone\'.');
jasmine['__zone_patch__'] = true;
var SyncTestZoneSpec = Zone['SyncTestZoneSpec'];
var ProxyZoneSpec = Zone['ProxyZoneSpec'];
if (!SyncTestZoneSpec)
throw new Error('Missing: SyncTestZoneSpec');
if (!ProxyZoneSpec)
throw new Error('Missing: ProxyZoneSpec');
var ambientZone = Zone.current;
// Create a synchronous-only zone in which to run `describe` blocks in order to raise an
// error if any asynchronous operations are attempted inside of a `describe` but outside of
// a `beforeEach` or `it`.
var syncZone = ambientZone.fork(new SyncTestZoneSpec('jasmine.describe'));
// This is the zone which will be used for running individual tests.
// It will be a proxy zone, so that the tests function can retroactively install
// different zones.
// Example:
// - In beforeEach() do childZone = Zone.current.fork(...);
// - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the
// zone outside of fakeAsync it will be able to escope the fakeAsync rules.
// - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add
// fakeAsync behavior to the childZone.
var testProxyZone = null;
// Monkey patch all of the jasmine DSL so that each function runs in appropriate zone.
var jasmineEnv = jasmine.getEnv();
['describe', 'xdescribe', 'fdescribe'].forEach(function (methodName) {
var originalJasmineFn = jasmineEnv[methodName];
jasmineEnv[methodName] = function (description, specDefinitions) {
return originalJasmineFn.call(this, description, wrapDescribeInZone(specDefinitions));
};
});
['it', 'xit', 'fit'].forEach(function (methodName) {
var originalJasmineFn = jasmineEnv[methodName];
jasmineEnv[methodName] = function (description, specDefinitions, timeout) {
arguments[1] = wrapTestInZone(specDefinitions);
return originalJasmineFn.apply(this, arguments);
};
});
['beforeEach', 'afterEach'].forEach(function (methodName) {
var originalJasmineFn = jasmineEnv[methodName];
jasmineEnv[methodName] = function (specDefinitions, timeout) {
arguments[0] = wrapTestInZone(specDefinitions);
return originalJasmineFn.apply(this, arguments);
};
});
/**
* Gets a function wrapping the body of a Jasmine `describe` block to execute in a
* synchronous-only zone.
*/
function wrapDescribeInZone(describeBody) {
return function () {
return syncZone.run(describeBody, this, arguments);
};
}
/**
* Gets a function wrapping the body of a Jasmine `it/beforeEach/afterEach` block to
* execute in a ProxyZone zone.
* This will run in `testProxyZone`. The `testProxyZone` will be reset by the `ZoneQueueRunner`
*/
function wrapTestInZone(testBody) {
// The `done` callback is only passed through if the function expects at least one argument.
// Note we have to make a function with correct number of arguments, otherwise jasmine will
// think that all functions are sync or async.
return (testBody.length == 0) ? function () {
return testProxyZone.run(testBody, this);
} : function (done) {
return testProxyZone.run(testBody, this, [done]);
};
}
var QueueRunner = jasmine.QueueRunner;
jasmine.QueueRunner = (function (_super) {
__extends(ZoneQueueRunner, _super);
function ZoneQueueRunner(attrs) {
attrs.onComplete = (function (fn) { return function () {
// All functions are done, clear the test zone.
testProxyZone = null;
ambientZone.scheduleMicroTask('jasmine.onComplete', fn);
}; })(attrs.onComplete);
_super.call(this, attrs);
}
ZoneQueueRunner.prototype.execute = function () {
var _this = this;
if (Zone.current !== ambientZone)
throw new Error('Unexpected Zone: ' + Zone.current.name);
testProxyZone = ambientZone.fork(new ProxyZoneSpec());
if (!Zone.currentTask) {
// if we are not running in a task then if someone would register a
// element.addEventListener and then calling element.click() the
// addEventListener callback would think that it is the top most task and would
// drain the microtask queue on element.click() which would be incorrect.
// For this reason we always force a task when running jasmine tests.
Zone.current.scheduleMicroTask('jasmine.execute().forceTask', function () { return QueueRunner.prototype.execute.call(_this); });
}
else {
_super.prototype.execute.call(this);
}
};
return ZoneQueueRunner;
}(QueueRunner));
})();
})));

View File

@@ -0,0 +1 @@
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(this,function(){"use strict";!function(){function e(e){return function(){return c.run(e,this,arguments)}}function n(e){return 0==e.length?function(){return u.run(e,this)}:function(n){return u.run(e,this,[n])}}var r=function(e,n){function r(){this.constructor=e}for(var t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)};if(!Zone)throw new Error("Missing: zone.js");if("undefined"==typeof jasmine)throw new Error("Missing: jasmine.js");if(jasmine.__zone_patch__)throw new Error("'jasmine' has already been patched with 'Zone'.");jasmine.__zone_patch__=!0;var t=Zone.SyncTestZoneSpec,o=Zone.ProxyZoneSpec;if(!t)throw new Error("Missing: SyncTestZoneSpec");if(!o)throw new Error("Missing: ProxyZoneSpec");var i=Zone.current,c=i.fork(new t("jasmine.describe")),u=null,s=jasmine.getEnv();["describe","xdescribe","fdescribe"].forEach(function(n){var r=s[n];s[n]=function(n,t){return r.call(this,n,e(t))}}),["it","xit","fit"].forEach(function(e){var r=s[e];s[e]=function(e,t,o){return arguments[1]=n(t),r.apply(this,arguments)}}),["beforeEach","afterEach"].forEach(function(e){var r=s[e];s[e]=function(e,t){return arguments[0]=n(e),r.apply(this,arguments)}});var f=jasmine.QueueRunner;jasmine.QueueRunner=function(e){function n(n){n.onComplete=function(e){return function(){u=null,i.scheduleMicroTask("jasmine.onComplete",e)}}(n.onComplete),e.call(this,n)}return r(n,e),n.prototype.execute=function(){var n=this;if(Zone.current!==i)throw new Error("Unexpected Zone: "+Zone.current.name);u=i.fork(new o),Zone.currentTask?e.prototype.execute.call(this):Zone.current.scheduleMicroTask("jasmine.execute().forceTask",function(){return f.prototype.execute.call(n)})},n}(f)}()});

View File

@@ -0,0 +1,157 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
(factory());
}(this, (function () { 'use strict';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
var NEWLINE = '\n';
var SEP = ' ------------- ';
var IGNORE_FRAMES = [];
var creationTrace = '__creationTrace__';
var LongStackTrace = (function () {
function LongStackTrace() {
this.error = getStacktrace();
this.timestamp = new Date();
}
return LongStackTrace;
}());
function getStacktraceWithUncaughtError() {
return new Error('STACKTRACE TRACKING');
}
function getStacktraceWithCaughtError() {
try {
throw getStacktraceWithUncaughtError();
}
catch (e) {
return e;
}
}
// Some implementations of exception handling don't create a stack trace if the exception
// isn't thrown, however it's faster not to actually throw the exception.
var error = getStacktraceWithUncaughtError();
var coughtError = getStacktraceWithCaughtError();
var getStacktrace = error.stack ?
getStacktraceWithUncaughtError :
(coughtError.stack ? getStacktraceWithCaughtError : getStacktraceWithUncaughtError);
function getFrames(error) {
return error.stack ? error.stack.split(NEWLINE) : [];
}
function addErrorStack(lines, error) {
var trace = getFrames(error);
for (var i = 0; i < trace.length; i++) {
var frame = trace[i];
// Filter out the Frames which are part of stack capturing.
if (!(i < IGNORE_FRAMES.length && IGNORE_FRAMES[i] === frame)) {
lines.push(trace[i]);
}
}
}
function renderLongStackTrace(frames, stack) {
var longTrace = [stack];
if (frames) {
var timestamp = new Date().getTime();
for (var i = 0; i < frames.length; i++) {
var traceFrames = frames[i];
var lastTime = traceFrames.timestamp;
longTrace.push(SEP + " Elapsed: " + (timestamp - lastTime.getTime()) + " ms; At: " + lastTime + " " + SEP);
addErrorStack(longTrace, traceFrames.error);
timestamp = lastTime.getTime();
}
}
return longTrace.join(NEWLINE);
}
Zone['longStackTraceZoneSpec'] = {
name: 'long-stack-trace',
longStackTraceLimit: 10,
onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) {
var currentTask = Zone.currentTask;
var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || [];
trace = [new LongStackTrace()].concat(trace);
if (trace.length > this.longStackTraceLimit) {
trace.length = this.longStackTraceLimit;
}
if (!task.data)
task.data = {};
task.data[creationTrace] = trace;
return parentZoneDelegate.scheduleTask(targetZone, task);
},
onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) {
var parentTask = Zone.currentTask || error.task;
if (error instanceof Error && parentTask) {
var stackSetSucceeded = null;
try {
var descriptor = Object.getOwnPropertyDescriptor(error, 'stack');
if (descriptor && descriptor.configurable) {
var delegateGet_1 = descriptor.get;
var value_1 = descriptor.value;
descriptor = {
get: function () {
return renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], delegateGet_1 ? delegateGet_1.apply(this) : value_1);
}
};
Object.defineProperty(error, 'stack', descriptor);
stackSetSucceeded = true;
}
}
catch (e) {
}
var longStack = stackSetSucceeded ?
null :
renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack);
if (!stackSetSucceeded) {
try {
stackSetSucceeded = error.stack = longStack;
}
catch (e) {
}
}
if (!stackSetSucceeded) {
try {
stackSetSucceeded = error.longStack = longStack;
}
catch (e) {
}
}
}
return parentZoneDelegate.handleError(targetZone, error);
}
};
function captureStackTraces(stackTraces, count) {
if (count > 0) {
stackTraces.push(getFrames((new LongStackTrace()).error));
captureStackTraces(stackTraces, count - 1);
}
}
function computeIgnoreFrames() {
var frames = [];
captureStackTraces(frames, 2);
var frames1 = frames[0];
var frames2 = frames[1];
for (var i = 0; i < frames1.length; i++) {
var frame1 = frames1[i];
var frame2 = frames2[i];
if (frame1 === frame2) {
IGNORE_FRAMES.push(frame1);
}
else {
break;
}
}
}
computeIgnoreFrames();
})));

Some files were not shown because too many files have changed in this diff Show More