blips
Version:
State management for the GraphQL heads
1,398 lines (1,307 loc) • 169 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('graphql')) :
typeof define === 'function' && define.amd ? define(['exports', 'graphql'], factory) :
(factory((global.blips = {}),global.GraphQL));
}(this, (function (exports,graphql) { 'use strict';
var graphql__default = 'default' in graphql ? graphql['default'] : graphql;
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var bld = createCommonjsModule(function (module, exports) {
/*
Deprecated Decorator v0.1
https://github.com/vilic/deprecated-decorator
*/
exports.options = {
getWarner: undefined
};
function createWarner(type, name, alternative, version, url) {
var warnedPositions = {};
return function () {
var stack = (new Error()).stack || '';
var at = (stack.match(/(?:\s+at\s.+){2}\s+at\s(.+)/) || [undefined, ''])[1];
if (/\)$/.test(at)) {
at = at.match(/[^(]+(?=\)$)/)[0];
}
else {
at = at.trim();
}
if (at in warnedPositions) {
return;
}
warnedPositions[at] = true;
var message;
switch (type) {
case 'class':
message = 'Class';
break;
case 'property':
message = 'Property';
break;
case 'method':
message = 'Method';
break;
case 'function':
message = 'Function';
break;
}
message += " `" + name + "` has been deprecated";
if (version) {
message += " since version " + version;
}
if (alternative) {
message += ", use `" + alternative + "` instead";
}
message += '.';
if (at) {
message += "\n at " + at;
}
if (url) {
message += "\nCheck out " + url + " for more information.";
}
console.warn(message);
};
}
function decorateProperty(type, name, descriptor, alternative, version, url) {
var warner = (exports.options.getWarner || createWarner)(type, name, alternative, version, url);
descriptor = descriptor || {
writable: true,
enumerable: false,
configurable: true
};
var deprecatedDescriptor = {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable
};
if (descriptor.get || descriptor.set) {
if (descriptor.get) {
deprecatedDescriptor.get = function () {
warner();
return descriptor.get.call(this);
};
}
if (descriptor.set) {
deprecatedDescriptor.set = function (value) {
warner();
return descriptor.set.call(this, value);
};
}
}
else {
var propertyValue_1 = descriptor.value;
deprecatedDescriptor.get = function () {
warner();
return propertyValue_1;
};
if (descriptor.writable) {
deprecatedDescriptor.set = function (value) {
warner();
propertyValue_1 = value;
};
}
}
return deprecatedDescriptor;
}
function decorateFunction(type, target, alternative, version, url) {
var name = target.name;
var warner = (exports.options.getWarner || createWarner)(type, name, alternative, version, url);
var fn = function () {
warner();
return target.apply(this, arguments);
};
for (var _i = 0, _a = Object.getOwnPropertyNames(target); _i < _a.length; _i++) {
var propertyName = _a[_i];
var descriptor = Object.getOwnPropertyDescriptor(target, propertyName);
if (descriptor.writable) {
fn[propertyName] = target[propertyName];
}
else if (descriptor.configurable) {
Object.defineProperty(fn, propertyName, descriptor);
}
}
return fn;
}
function deprecated() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i - 0] = arguments[_i];
}
var fn = args[args.length - 1];
if (typeof fn === 'function') {
fn = args.pop();
}
else {
fn = undefined;
}
var options = args[0];
var alternative;
var version;
var url;
if (typeof options === 'string') {
alternative = options;
version = args[1];
url = args[2];
}
else if (options) {
(alternative = options.alternative, version = options.version, url = options.url, options);
}
if (fn) {
return decorateFunction('function', fn, alternative, version, url);
}
return function (target, name, descriptor) {
if (typeof name === 'string') {
var type = descriptor && typeof descriptor.value === 'function' ?
'method' : 'property';
return decorateProperty(type, name, descriptor, alternative, version, url);
}
else if (typeof target === 'function') {
var constructor = decorateFunction('class', target, alternative, version, url);
var className = target.name;
for (var _i = 0, _a = Object.getOwnPropertyNames(constructor); _i < _a.length; _i++) {
var propertyName = _a[_i];
var descriptor_1 = Object.getOwnPropertyDescriptor(constructor, propertyName);
descriptor_1 = decorateProperty('class', className, descriptor_1, alternative, version, url);
if (descriptor_1.writable) {
constructor[propertyName] = target[propertyName];
}
else if (descriptor_1.configurable) {
Object.defineProperty(constructor, propertyName, descriptor_1);
}
}
return constructor;
}
};
}
exports.deprecated = deprecated;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = deprecated;
});
unwrapExports(bld);
var bld_1 = bld.options;
var bld_2 = bld.deprecated;
var schemaGenerator = createCommonjsModule(function (module, exports) {
var __extends = (commonjsGlobal && commonjsGlobal.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
// TODO: document each function clearly in the code: what arguments it accepts
// and what it outputs.
// TODO: we should refactor this file, rename it to makeExecutableSchema, and move
// a bunch of utility functions into a separate utitlities folder, one file per function.
// @schemaDefinition: A GraphQL type schema in shorthand
// @resolvers: Definitions for resolvers to be merged with schema
var SchemaError = /** @class */ (function (_super) {
__extends(SchemaError, _super);
function SchemaError(message) {
var _this = _super.call(this, message) || this;
_this.message = message;
Error.captureStackTrace(_this, _this.constructor);
return _this;
}
return SchemaError;
}(Error));
exports.SchemaError = SchemaError;
// type definitions can be a string or an array of strings.
function _generateSchema(typeDefinitions, resolveFunctions, logger,
// TODO: rename to allowUndefinedInResolve to be consistent
allowUndefinedInResolve, resolverValidationOptions) {
if (typeof resolverValidationOptions !== 'object') {
throw new SchemaError('Expected `resolverValidationOptions` to be an object');
}
if (!typeDefinitions) {
throw new SchemaError('Must provide typeDefs');
}
if (!resolveFunctions) {
throw new SchemaError('Must provide resolvers');
}
// TODO: check that typeDefinitions is either string or array of strings
var schema = buildSchemaFromTypeDefinitions(typeDefinitions);
addResolveFunctionsToSchema(schema, resolveFunctions, resolverValidationOptions);
assertResolveFunctionsPresent(schema, resolverValidationOptions);
if (!allowUndefinedInResolve) {
addCatchUndefinedToSchema(schema);
}
if (logger) {
addErrorLoggingToSchema(schema, logger);
}
return schema;
}
function makeExecutableSchema(_a) {
var typeDefs = _a.typeDefs, _b = _a.resolvers, resolvers = _b === void 0 ? {} : _b, connectors = _a.connectors, logger = _a.logger, _c = _a.allowUndefinedInResolve, allowUndefinedInResolve = _c === void 0 ? true : _c, _d = _a.resolverValidationOptions, resolverValidationOptions = _d === void 0 ? {} : _d;
var jsSchema = _generateSchema(typeDefs, resolvers, logger, allowUndefinedInResolve, resolverValidationOptions);
if (typeof resolvers['__schema'] === 'function') {
// TODO a bit of a hack now, better rewrite generateSchema to attach it there.
// not doing that now, because I'd have to rewrite a lot of tests.
addSchemaLevelResolveFunction(jsSchema, resolvers['__schema']);
}
if (connectors) {
// connectors are optional, at least for now. That means you can just import them in the resolve
// function if you want.
attachConnectorsToContext(jsSchema, connectors);
}
return jsSchema;
}
exports.makeExecutableSchema = makeExecutableSchema;
function isDocumentNode(typeDefinitions) {
return typeDefinitions.kind !== undefined;
}
function uniq(array) {
return array.reduce(function (accumulator, currentValue) {
return accumulator.indexOf(currentValue) === -1
? accumulator.concat([currentValue]) : accumulator;
}, []);
}
function concatenateTypeDefs(typeDefinitionsAry, calledFunctionRefs) {
if (calledFunctionRefs === void 0) { calledFunctionRefs = []; }
var resolvedTypeDefinitions = [];
typeDefinitionsAry.forEach(function (typeDef) {
if (isDocumentNode(typeDef)) {
typeDef = graphql__default.print(typeDef);
}
if (typeof typeDef === 'function') {
if (calledFunctionRefs.indexOf(typeDef) === -1) {
calledFunctionRefs.push(typeDef);
resolvedTypeDefinitions = resolvedTypeDefinitions.concat(concatenateTypeDefs(typeDef(), calledFunctionRefs));
}
}
else if (typeof typeDef === 'string') {
resolvedTypeDefinitions.push(typeDef.trim());
}
else {
var type = typeof typeDef;
throw new SchemaError("typeDef array must contain only strings and functions, got " + type);
}
});
return uniq(resolvedTypeDefinitions.map(function (x) { return x.trim(); })).join('\n');
}
exports.concatenateTypeDefs = concatenateTypeDefs;
function buildSchemaFromTypeDefinitions(typeDefinitions) {
// TODO: accept only array here, otherwise interfaces get confusing.
var myDefinitions = typeDefinitions;
var astDocument;
if (isDocumentNode(typeDefinitions)) {
astDocument = typeDefinitions;
}
else if (typeof myDefinitions !== 'string') {
if (!Array.isArray(myDefinitions)) {
var type = typeof myDefinitions;
throw new SchemaError("typeDefs must be a string, array or schema AST, got " + type);
}
myDefinitions = concatenateTypeDefs(myDefinitions);
}
if (typeof myDefinitions === 'string') {
astDocument = graphql__default.parse(myDefinitions);
}
var schema = graphql__default.buildASTSchema(astDocument);
var extensionsAst = extractExtensionDefinitions(astDocument);
if (extensionsAst.definitions.length > 0) {
schema = graphql__default.extendSchema(schema, extensionsAst);
}
return schema;
}
exports.buildSchemaFromTypeDefinitions = buildSchemaFromTypeDefinitions;
function extractExtensionDefinitions(ast) {
var extensionDefs = ast.definitions.filter(function (def) { return def.kind === graphql__default.Kind.TYPE_EXTENSION_DEFINITION; });
return Object.assign({}, ast, {
definitions: extensionDefs,
});
}
exports.extractExtensionDefinitions = extractExtensionDefinitions;
function forEachField(schema, fn) {
var typeMap = schema.getTypeMap();
Object.keys(typeMap).forEach(function (typeName) {
var type = typeMap[typeName];
// TODO: maybe have an option to include these?
if (!graphql__default.getNamedType(type).name.startsWith('__') &&
type instanceof graphql__default.GraphQLObjectType) {
var fields_1 = type.getFields();
Object.keys(fields_1).forEach(function (fieldName) {
var field = fields_1[fieldName];
fn(field, typeName, fieldName);
});
}
});
}
exports.forEachField = forEachField;
// takes a GraphQL-JS schema and an object of connectors, then attaches
// the connectors to the context by wrapping each query or mutation resolve
// function with a function that attaches connectors if they don't exist.
// attaches connectors only once to make sure they are singletons
var attachConnectorsToContext = bld.deprecated({
version: '0.7.0',
url: 'https://github.com/apollostack/graphql-tools/issues/140',
}, function (schema, connectors) {
if (!schema || !(schema instanceof graphql__default.GraphQLSchema)) {
throw new Error('schema must be an instance of GraphQLSchema. ' +
'This error could be caused by installing more than one version of GraphQL-JS');
}
if (typeof connectors !== 'object') {
var connectorType = typeof connectors;
throw new Error("Expected connectors to be of type object, got " + connectorType);
}
if (Object.keys(connectors).length === 0) {
throw new Error('Expected connectors to not be an empty object');
}
if (Array.isArray(connectors)) {
throw new Error('Expected connectors to be of type object, got Array');
}
if (schema['_apolloConnectorsAttached']) {
throw new Error('Connectors already attached to context, cannot attach more than once');
}
schema['_apolloConnectorsAttached'] = true;
var attachconnectorFn = function (root, args, ctx) {
if (typeof ctx !== 'object') {
// if in any way possible, we should throw an error when the attachconnectors
// function is called, not when a query is executed.
var contextType = typeof ctx;
throw new Error("Cannot attach connector because context is not an object: " + contextType);
}
if (typeof ctx.connectors === 'undefined') {
ctx.connectors = {};
}
Object.keys(connectors).forEach(function (connectorName) {
var connector = connectors[connectorName];
if (!!connector.prototype) {
ctx.connectors[connectorName] = new connector(ctx);
}
else {
throw new Error("Connector must be a function or an class");
}
});
return root;
};
addSchemaLevelResolveFunction(schema, attachconnectorFn);
});
exports.attachConnectorsToContext = attachConnectorsToContext;
// wraps all resolve functions of query, mutation or subscription fields
// with the provided function to simulate a root schema level resolve funciton
function addSchemaLevelResolveFunction(schema, fn) {
// TODO test that schema is a schema, fn is a function
var rootTypes = [
schema.getQueryType(),
schema.getMutationType(),
schema.getSubscriptionType(),
].filter(function (x) { return !!x; });
rootTypes.forEach(function (type) {
// XXX this should run at most once per request to simulate a true root resolver
// for graphql-js this is an approximation that works with queries but not mutations
var rootResolveFn = runAtMostOncePerRequest(fn);
var fields = type.getFields();
Object.keys(fields).forEach(function (fieldName) {
// XXX if the type is a subscription, a same query AST will be ran multiple times so we
// deactivate here the runOnce if it's a subscription. This may not be optimal though...
if (type === schema.getSubscriptionType()) {
fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, fn);
}
else {
fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, rootResolveFn);
}
});
});
}
exports.addSchemaLevelResolveFunction = addSchemaLevelResolveFunction;
function getFieldsForType(type) {
if (type instanceof graphql__default.GraphQLObjectType ||
type instanceof graphql__default.GraphQLInterfaceType) {
return type.getFields();
}
else {
return undefined;
}
}
function addResolveFunctionsToSchema(schema, resolveFunctions, resolverValidationOptions) {
if (resolverValidationOptions === void 0) { resolverValidationOptions = {}; }
var _a = resolverValidationOptions.allowResolversNotInSchema, allowResolversNotInSchema = _a === void 0 ? false : _a;
Object.keys(resolveFunctions).forEach(function (typeName) {
var type = schema.getType(typeName);
if (!type && typeName !== '__schema') {
if (allowResolversNotInSchema) {
return;
}
throw new SchemaError("\"" + typeName + "\" defined in resolvers, but not in schema");
}
Object.keys(resolveFunctions[typeName]).forEach(function (fieldName) {
if (fieldName.startsWith('__')) {
// this is for isTypeOf and resolveType and all the other stuff.
// TODO require resolveType for unions and interfaces.
type[fieldName.substring(2)] = resolveFunctions[typeName][fieldName];
return;
}
if (type instanceof graphql__default.GraphQLScalarType) {
type[fieldName] = resolveFunctions[typeName][fieldName];
return;
}
if (type instanceof graphql__default.GraphQLEnumType) {
// TODO: Remove once https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21786
// is inside NPM
if (!type.isValidValue(fieldName)) {
throw new SchemaError(typeName + "." + fieldName + " was defined in resolvers, but enum is not in schema");
}
type.getValue(fieldName)['value'] = resolveFunctions[typeName][fieldName];
return;
}
var fields = getFieldsForType(type);
if (!fields) {
if (allowResolversNotInSchema) {
return;
}
throw new SchemaError(typeName + " was defined in resolvers, but it's not an object");
}
if (!fields[fieldName]) {
if (allowResolversNotInSchema) {
return;
}
throw new SchemaError(typeName + "." + fieldName + " defined in resolvers, but not in schema");
}
var field = fields[fieldName];
var fieldResolve = resolveFunctions[typeName][fieldName];
if (typeof fieldResolve === 'function') {
// for convenience. Allows shorter syntax in resolver definition file
setFieldProperties(field, { resolve: fieldResolve });
}
else {
if (typeof fieldResolve !== 'object') {
throw new SchemaError("Resolver " + typeName + "." + fieldName + " must be object or function");
}
setFieldProperties(field, fieldResolve);
}
});
});
}
exports.addResolveFunctionsToSchema = addResolveFunctionsToSchema;
function setFieldProperties(field, propertiesObj) {
Object.keys(propertiesObj).forEach(function (propertyName) {
field[propertyName] = propertiesObj[propertyName];
});
}
function assertResolveFunctionsPresent(schema, resolverValidationOptions) {
if (resolverValidationOptions === void 0) { resolverValidationOptions = {}; }
var _a = resolverValidationOptions.requireResolversForArgs, requireResolversForArgs = _a === void 0 ? false : _a, _b = resolverValidationOptions.requireResolversForNonScalar, requireResolversForNonScalar = _b === void 0 ? false : _b, _c = resolverValidationOptions.requireResolversForAllFields, requireResolversForAllFields = _c === void 0 ? false : _c;
if (requireResolversForAllFields &&
(requireResolversForArgs || requireResolversForNonScalar)) {
throw new TypeError('requireResolversForAllFields takes precedence over the more specific assertions. ' +
'Please configure either requireResolversForAllFields or requireResolversForArgs / ' +
'requireResolversForNonScalar, but not a combination of them.');
}
forEachField(schema, function (field, typeName, fieldName) {
// requires a resolve function for *every* field.
if (requireResolversForAllFields) {
expectResolveFunction(field, typeName, fieldName);
}
// requires a resolve function on every field that has arguments
if (requireResolversForArgs && field.args.length > 0) {
expectResolveFunction(field, typeName, fieldName);
}
// requires a resolve function on every field that returns a non-scalar type
if (requireResolversForNonScalar &&
!(graphql__default.getNamedType(field.type) instanceof graphql__default.GraphQLScalarType)) {
expectResolveFunction(field, typeName, fieldName);
}
});
}
exports.assertResolveFunctionsPresent = assertResolveFunctionsPresent;
function expectResolveFunction(field, typeName, fieldName) {
if (!field.resolve) {
console.warn(
// tslint:disable-next-line: max-line-length
"Resolve function missing for \"" + typeName + "." + fieldName + "\". To disable this warning check https://github.com/apollostack/graphql-tools/issues/131");
return;
}
if (typeof field.resolve !== 'function') {
throw new SchemaError("Resolver \"" + typeName + "." + fieldName + "\" must be a function");
}
}
function addErrorLoggingToSchema(schema, logger) {
if (!logger) {
throw new Error('Must provide a logger');
}
if (typeof logger.log !== 'function') {
throw new Error('Logger.log must be a function');
}
forEachField(schema, function (field, typeName, fieldName) {
var errorHint = typeName + "." + fieldName;
field.resolve = decorateWithLogger(field.resolve, logger, errorHint);
});
}
exports.addErrorLoggingToSchema = addErrorLoggingToSchema;
// XXX badly named function. this doesn't really wrap, it just chains resolvers...
function wrapResolver(innerResolver, outerResolver) {
return function (obj, args, ctx, info) {
return Promise.resolve(outerResolver(obj, args, ctx, info)).then(function (root) {
if (innerResolver) {
return innerResolver(root, args, ctx, info);
}
return graphql__default.defaultFieldResolver(root, args, ctx, info);
});
};
}
function chainResolvers(resolvers) {
return function (root, args, ctx, info) {
return resolvers.reduce(function (prev, curResolver) {
if (curResolver) {
return curResolver(prev, args, ctx, info);
}
return graphql__default.defaultFieldResolver(prev, args, ctx, info);
}, root);
};
}
exports.chainResolvers = chainResolvers;
/*
* fn: The function to decorate with the logger
* logger: an object instance of type Logger
* hint: an optional hint to add to the error's message
*/
function decorateWithLogger(fn, logger, hint) {
if (typeof fn === 'undefined') {
fn = graphql__default.defaultFieldResolver;
}
var logError = function (e) {
// TODO: clone the error properly
var newE = new Error();
newE.stack = e.stack;
/* istanbul ignore else: always get the hint from addErrorLoggingToSchema */
if (hint) {
newE['originalMessage'] = e.message;
newE['message'] = "Error in resolver " + hint + "\n" + e.message;
}
logger.log(newE);
};
return function (root, args, ctx, info) {
try {
var result = fn(root, args, ctx, info);
// If the resolve function returns a Promise log any Promise rejects.
if (result &&
typeof result.then === 'function' &&
typeof result.catch === 'function') {
result.catch(function (reason) {
// make sure that it's an error we're logging.
var error = reason instanceof Error ? reason : new Error(reason);
logError(error);
// We don't want to leave an unhandled exception so pass on error.
return reason;
});
}
return result;
}
catch (e) {
logError(e);
// we want to pass on the error, just in case.
throw e;
}
};
}
function addCatchUndefinedToSchema(schema) {
forEachField(schema, function (field, typeName, fieldName) {
var errorHint = typeName + "." + fieldName;
field.resolve = decorateToCatchUndefined(field.resolve, errorHint);
});
}
exports.addCatchUndefinedToSchema = addCatchUndefinedToSchema;
function decorateToCatchUndefined(fn, hint) {
if (typeof fn === 'undefined') {
fn = graphql__default.defaultFieldResolver;
}
return function (root, args, ctx, info) {
var result = fn(root, args, ctx, info);
if (typeof result === 'undefined') {
throw new Error("Resolve function for \"" + hint + "\" returned undefined");
}
return result;
};
}
// XXX this function only works for resolvers
// XXX very hacky way to remember if the function
// already ran for this request. This will only work
// if people don't actually cache the operation.
// if they do cache the operation, they will have to
// manually remove the __runAtMostOnce before every request.
function runAtMostOncePerRequest(fn) {
var value;
var randomNumber = Math.random();
return function (root, args, ctx, info) {
if (!info.operation['__runAtMostOnce']) {
info.operation['__runAtMostOnce'] = {};
}
if (!info.operation['__runAtMostOnce'][randomNumber]) {
info.operation['__runAtMostOnce'][randomNumber] = true;
value = fn(root, args, ctx, info);
}
return value;
};
}
});
unwrapExports(schemaGenerator);
var schemaGenerator_1 = schemaGenerator.SchemaError;
var schemaGenerator_2 = schemaGenerator.makeExecutableSchema;
var schemaGenerator_3 = schemaGenerator.concatenateTypeDefs;
var schemaGenerator_4 = schemaGenerator.buildSchemaFromTypeDefinitions;
var schemaGenerator_5 = schemaGenerator.extractExtensionDefinitions;
var schemaGenerator_6 = schemaGenerator.forEachField;
var schemaGenerator_7 = schemaGenerator.attachConnectorsToContext;
var schemaGenerator_8 = schemaGenerator.addSchemaLevelResolveFunction;
var schemaGenerator_9 = schemaGenerator.addResolveFunctionsToSchema;
var schemaGenerator_10 = schemaGenerator.assertResolveFunctionsPresent;
var schemaGenerator_11 = schemaGenerator.addErrorLoggingToSchema;
var schemaGenerator_12 = schemaGenerator.chainResolvers;
var schemaGenerator_13 = schemaGenerator.addCatchUndefinedToSchema;
var domain;
// This constructor is used to store event handlers. Instantiating this is
// faster than explicitly calling `Object.create(null)` to get a "clean" empty
// object (tested with v8 v4.9).
function EventHandlers() {}
EventHandlers.prototype = Object.create(null);
function EventEmitter() {
EventEmitter.init.call(this);
}
// nodejs oddity
// require('events') === require('events').EventEmitter
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.usingDomains = false;
EventEmitter.prototype.domain = undefined;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
EventEmitter.init = function() {
this.domain = null;
if (EventEmitter.usingDomains) {
// if there is an active domain, then attach to it.
if (domain.active && !(this instanceof domain.Domain)) {
this.domain = domain.active;
}
}
if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
this._events = new EventHandlers();
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || isNaN(n))
throw new TypeError('"n" argument must be a positive number');
this._maxListeners = n;
return this;
};
function $getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return $getMaxListeners(this);
};
// These standalone emit* functions are used to optimize calling of event
// handlers for fast cases because emit() itself often has a variable number of
// arguments and can be deoptimized because of that. These functions always have
// the same number of arguments and thus do not get deoptimized, so the code
// inside them can execute faster.
function emitNone(handler, isFn, self) {
if (isFn)
handler.call(self);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self);
}
}
function emitOne(handler, isFn, self, arg1) {
if (isFn)
handler.call(self, arg1);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1);
}
}
function emitTwo(handler, isFn, self, arg1, arg2) {
if (isFn)
handler.call(self, arg1, arg2);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2);
}
}
function emitThree(handler, isFn, self, arg1, arg2, arg3) {
if (isFn)
handler.call(self, arg1, arg2, arg3);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2, arg3);
}
}
function emitMany(handler, isFn, self, args) {
if (isFn)
handler.apply(self, args);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].apply(self, args);
}
}
EventEmitter.prototype.emit = function emit(type) {
var er, handler, len, args, i, events, domain;
var needDomainExit = false;
var doError = (type === 'error');
events = this._events;
if (events)
doError = (doError && events.error == null);
else if (!doError)
return false;
domain = this.domain;
// If there is no 'error' event listener then throw.
if (doError) {
er = arguments[1];
if (domain) {
if (!er)
er = new Error('Uncaught, unspecified "error" event');
er.domainEmitter = this;
er.domain = domain;
er.domainThrown = false;
domain.emit('error', er);
} else if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
return false;
}
handler = events[type];
if (!handler)
return false;
var isFn = typeof handler === 'function';
len = arguments.length;
switch (len) {
// fast cases
case 1:
emitNone(handler, isFn, this);
break;
case 2:
emitOne(handler, isFn, this, arguments[1]);
break;
case 3:
emitTwo(handler, isFn, this, arguments[1], arguments[2]);
break;
case 4:
emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
break;
// slower
default:
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
emitMany(handler, isFn, this, args);
}
if (needDomainExit)
domain.exit();
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = target._events;
if (!events) {
events = target._events = new EventHandlers();
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (!existing) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] = prepend ? [listener, existing] :
[existing, listener];
} else {
// If we've already got an array, just append.
if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
}
// Check for listener leak
if (!existing.warned) {
m = $getMaxListeners(target);
if (m && m > 0 && existing.length > m) {
existing.warned = true;
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + type + ' listeners added. ' +
'Use emitter.setMaxListeners() to increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
emitWarning(w);
}
}
}
return target;
}
function emitWarning(e) {
typeof console.warn === 'function' ? console.warn(e) : console.log(e);
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function _onceWrap(target, type, listener) {
var fired = false;
function g() {
target.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(target, arguments);
}
}
g.listener = listener;
return g;
}
EventEmitter.prototype.once = function once(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = this._events;
if (!events)
return this;
list = events[type];
if (!list)
return this;
if (list === listener || (list.listener && list.listener === listener)) {
if (--this._eventsCount === 0)
this._events = new EventHandlers();
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list[0] = undefined;
if (--this._eventsCount === 0) {
this._events = new EventHandlers();
return this;
} else {
delete events[type];
}
} else {
spliceOne(list, position);
}
if (events.removeListener)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events;
events = this._events;
if (!events)
return this;
// not listening for removeListener, no need to emit
if (!events.removeListener) {
if (arguments.length === 0) {
this._events = new EventHandlers();
this._eventsCount = 0;
} else if (events[type]) {
if (--this._eventsCount === 0)
this._events = new EventHandlers();
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
for (var i = 0, key; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = new EventHandlers();
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
do {
this.removeListener(type, listeners[listeners.length - 1]);
} while (listeners[0]);
}
return this;
};
EventEmitter.prototype.listeners = function listeners(type) {
var evlistener;
var ret;
var events = this._events;
if (!events)
ret = [];
else {
evlistener = events[type];
if (!evlistener)
ret = [];
else if (typeof evlistener === 'function')
ret = [evlistener.listener || evlistener];
else
ret = unwrapListeners(evlistener);
}
return ret;
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
};
// About 1.5x faster than the two-arg version of Array#splice().
function spliceOne(list, index) {
for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
list[i] = list[k];
list.pop();
}
function arrayClone(arr, i) {
var copy = new Array(i);
while (i--)
copy[i] = arr[i];
return copy;
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
var events = Object.freeze({
default: EventEmitter,
EventEmitter: EventEmitter
});
/**
* Copyright (c) 2016, Lee Byron
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @ignore
*/
/**
* [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterator)
* is a *protocol* which describes a standard way to produce a sequence of
* values, typically the values of the Iterable represented by this Iterator.
*
* While described by the [ES2015 version of JavaScript](http://www.ecma-international.org/ecma-262/6.0/#sec-iterator-interface)
* it can be utilized by any version of JavaScript.
*
* @typedef {Object} Iterator
* @template T The type of each iterated value
* @property {function (): { value: T, done: boolean }} next
* A method which produces either the next value in a sequence or a result
* where the `done` property is `true` indicating the end of the Iterator.
*/
/**
* [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterable)
* is a *protocol* which when implemented allows a JavaScript object to define
* their iteration behavior, such as what values are looped over in a `for..of`
* loop or `iterall`'s `forEach` function. Many [built-in types](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#Builtin_iterables)
* implement the Iterable protocol, including `Array` and `Map`.
*
* While described by the [ES2015 version of JavaScript](http://www.ecma-international.org/ecma-262/6.0/#sec-iterable-interface)
* it can be utilized by any version of JavaScript.
*
* @typedef {Object} Iterable
* @template T The type of each iterated value
* @property {function (): Iterator<T>} Symbol.iterator
* A method which produces an Iterator for this Iterable.
*/
// In ES2015 (or a polyfilled) environment, this will be Symbol.iterator
var SYMBOL_ITERATOR = typeof Symbol === 'function' && Symbol.iterator;
/**
* A property name to be used as the name of an Iterable's method responsible
* for producing an Iterator, referred to as `@@iterator`. Typically represents
* the value `Symbol.iterator` but falls back to the string `"@@iterator"` when
* `Symbol.iterator` is not defined.
*
* Use `$$iterator` for defining new Iterables instead of `Symbol.iterator`,
* but do not use it for accessing existing Iterables, instead use
* `getIterator()` or `isIterable()`.
*
* @example
*
* var $$iterator = require('iterall').$$iterator
*
* function Counter (to) {
* this.to = to
* }
*
* Counter.prototype[$$iterator] = function () {
* return {
* to: this.to,
* num: 0,
* next () {
* if (this.num >= this.to) {
* return { value: undefined, done: true }
* }
* return { value: this.num++, done: false }
* }
* }
* }
*
* var counter = new Counter(3)
* for (var number of counter) {
* console.log(number) // 0 ... 1 ... 2
* }
*
* @type {Symbol|string}
*/
var $$iterator = SYMBOL_ITERATOR || '@@iterator';
var $$iterator_1 = $$iterator;
/**
* Returns true if the provided object implements the Iterator protocol via
* either implementing a `Symbol.iterator` or `"@@iterator"` method.
*
* @example
*
* var isIterable = require('iterall').isIterable
* isIterable([ 1, 2, 3 ]) // true
* isIterable('ABC') // true
* isIterable({ length: 1, 0: 'Alpha' }) // false
* isIterable({ key: 'value' }) // false
* isIterable(new Map()) // true
*
* @param obj
* A value which might implement the Iterable protocol.
* @return {boolean} true if Iterable.
*/
function isIterable(obj) {
return !!getIteratorMethod(obj)
}
var isIterable_1 = isIterable;
/**
* Returns true if the provided object implements the Array-like protocol via
* defining a positive-integer `length` property.
*
* @example
*
* var isArrayLike = require('iterall').isArrayLike
* isArrayLike([ 1, 2, 3 ]) // true
* isArrayLike('ABC') // true
* isArrayLike({ length: 1, 0: 'Alpha' }) // true
* isArrayLike({ key: 'value' }) // false
* isArrayLike(new Map()) // false
*
* @param obj
* A value which might implement the Array-like protocol.
* @return {boolean} true if Array-like.
*/
function isArrayLike(obj) {
var length = obj != null && obj.length;
return typeof length === 'number' && length >= 0 && length % 1 === 0
}
var isArrayLike_1 = isArrayLike;
/**
* Returns true if the provided object is an Object (i.e. not a string literal)
* and is either Iterable or Array-like.
*
* This may be used in place of [Array.isArray()][isArray] to determine if an
* object should be iterated-over. It always excludes string literals and
* includes Arrays (regardless of if it is Iterable). It also includes other
* Array-like objects such as NodeList, TypedArray, and Buffer.
*
* @example
*
* var isCollection = require('iterall').isCollection
* isCollection([ 1, 2, 3 ]) // true
* isCollection('ABC') // false
* isCollection({ length: 1, 0: 'Alpha' }) // true
* isCollection({ key: 'value' }) // false
* isCollection(new Map()) // true
*
* @example
*
* var forEach = require('iterall').forEach
* if (isCollection(obj)) {
* forEach(obj, function (value) {
* console.log(value)
* })
* }
*
* @param obj
* An Object value which might implement the Iterable or Array-like protocols.
* @return {boolean} true if Iterable or Array-like Object.
*/
function isCollection(obj) {
return Object(obj) === obj && (isArrayLike(obj) || isIterable(obj))
}
var isCollection_1 = isCollection;
/**
* If the provided object implements the Iterator protocol, its Iterator object
* is returned. Otherwise returns undefined.
*
* @example
*
* var getIterator = require('iterall').getIterator
* var iterator = getIterator([ 1, 2, 3 ])
* iterator.next() // { value: 1, done: false }
* iterator.next() // { value: 2, done: false }
* iterator.next() // { value: 3, done: false }
* iterator.next() // { value: undefined, done: true }
*
* @template T the type of each iterated value
* @param {Iterable<T>} iterable
* An Iterable object which is the source of an Iterator.
* @return {Iterator<T>} new Iterator instance.
*/
function getIterator(iterable) {
var method = getIteratorMethod(iterable);
if (method) {
return method.call(iterable)
}
}
var getIterator_1 = getIterator;
/**
* If the provided object implements the Iterator protocol, the method
* responsible for producing its Iterator object is returned.
*
* This is used in rare cases for performance tuning. This method must be called
* with obj as the contextual this-argument.
*
* @example
*
* var getIteratorMethod = require('iterall').getIteratorMethod
* var myArray = [ 1, 2, 3 ]
* var method = getIteratorMethod(myArray)
* if (method) {
* var iterator = method.call(myArray)
* }
*
* @template T the type of each iterated value
* @param {Iterable<T>} iterable
* An Iterable object which defines an `@@iterator` method.
* @return {function(): Iterator<T>} `@@iterator` method.
*/
function getIteratorMethod(iterable) {
if (iterable != null) {
var method =
(SYMBOL_ITERATOR && iterable[SYMBOL_ITERATOR]) || iterable['@@iterator'];
if (typeof method === 'function') {
return method
}
}
}
var getIteratorMethod_1 = getIteratorMethod;
/**
* Similar to `getIterator()`, this method returns a new Iterator given an
* Iterable. However it will also create an Iterator for a non-Iterable
* Array-like collection, such as Array in a non-ES2015 environment.
*
* `createIterator` is complimentary to `forEach`, but allows a "pull"-based
* iteration as opposed to `forEach`'s "push"-based iteration.
*
* `createIterator` produces an Iterator for Array-likes with the same behavior
* as ArrayIteratorPrototype described in the ECMAScript specification, and
* does *not* skip over "holes".
*
* @example
*
* var createIterator = require('iterall').createIterator
*
* var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' }
* var iterator = createIterator(myArraylike)
* iterator.next() // { value: 'Alpha', done: false }
* iterator.next() // { value: 'Bravo', done: false }
* iterator.next() // { value: 'Charlie', done: false }
* iterator.next() // { value: undefined, done: true }
*
* @template T the type of each iterated value
* @param {Iterable<T>|{ length: number }} collection