UNPKG

blips

Version:

State management for the GraphQL heads

1,398 lines (1,307 loc) 169 kB
(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