{"version":3,"sources":["meteor://💻app/packages/callback-hook/hook.js"],"names":["module","export","Hook","hasOwn","Object","prototype","hasOwnProperty","constructor","options","nextCallbackId","callbacks","create","bindEnvironment","exceptionHandler","debugPrintExceptions","Error","register","callback","exception","Meteor","dontBindEnvironment","id","stop","forEach","iterator","_nodeCodeMustBeInFiber","ids","keys","i","length","call","each","func","onException","_this","description","error","_debug","ret","args","apply","e"],"mappings":"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","file":"/packages/callback-hook.js","sourcesContent":["// XXX This pattern is under development. Do not add more callsites\n// using this package for now. See:\n// https://meteor.hackpad.com/Design-proposal-Hooks-YxvgEW06q6f\n//\n// Encapsulates the pattern of registering callbacks on a hook.\n//\n// The `each` method of the hook calls its iterator function argument\n// with each registered callback. This allows the hook to\n// conditionally decide not to call the callback (if, for example, the\n// observed object has been closed or terminated).\n//\n// By default, callbacks are bound with `Meteor.bindEnvironment`, so they will be\n// called with the Meteor environment of the calling code that\n// registered the callback. Override by passing { bindEnvironment: false }\n// to the constructor.\n//\n// Registering a callback returns an object with a single `stop`\n// method which unregisters the callback.\n//\n// The code is careful to allow a callback to be safely unregistered\n// while the callbacks are being iterated over.\n//\n// If the hook is configured with the `exceptionHandler` option, the\n// handler will be called if a called callback throws an exception.\n// By default (if the exception handler doesn't itself throw an\n// exception, or if the iterator function doesn't return a falsy value\n// to terminate the calling of callbacks), the remaining callbacks\n// will still be called.\n//\n// Alternatively, the `debugPrintExceptions` option can be specified\n// as string describing the callback. On an exception the string and\n// the exception will be printed to the console log with\n// `Meteor._debug`, and the exception otherwise ignored.\n//\n// If an exception handler isn't specified, exceptions thrown in the\n// callback will propagate up to the iterator function, and will\n// terminate calling the remaining callbacks if not caught.\n\nconst hasOwn = Object.prototype.hasOwnProperty;\n\nexport class Hook {\n constructor(options) {\n options = options || {};\n this.nextCallbackId = 0;\n this.callbacks = Object.create(null);\n // Whether to wrap callbacks with Meteor.bindEnvironment\n this.bindEnvironment = true;\n if (options.bindEnvironment === false) {\n this.bindEnvironment = false;\n }\n\n if (options.exceptionHandler) {\n this.exceptionHandler = options.exceptionHandler;\n } else if (options.debugPrintExceptions) {\n if (typeof options.debugPrintExceptions !== \"string\") {\n throw new Error(\"Hook option debugPrintExceptions should be a string\");\n }\n this.exceptionHandler = options.debugPrintExceptions;\n }\n }\n\n register(callback) {\n const exceptionHandler = this.exceptionHandler || function (exception) {\n // Note: this relies on the undocumented fact that if bindEnvironment's\n // onException throws, and you are invoking the callback either in the\n // browser or from within a Fiber in Node, the exception is propagated.\n throw exception;\n };\n\n if (this.bindEnvironment) {\n callback = Meteor.bindEnvironment(callback, exceptionHandler);\n } else {\n callback = dontBindEnvironment(callback, exceptionHandler);\n }\n\n const id = this.nextCallbackId++;\n this.callbacks[id] = callback;\n\n return {\n callback,\n stop: () => {\n delete this.callbacks[id];\n }\n };\n }\n\n /**\n * For each registered callback, call the passed iterator function with the callback.\n *\n * The iterator function can choose whether or not to call the\n * callback. (For example, it might not call the callback if the\n * observed object has been closed or terminated).\n * The iteration is stopped if the iterator function returns a falsy\n * value or throws an exception.\n *\n * @param iterator\n */\n forEach(iterator) {\n // Invoking bindEnvironment'd callbacks outside of a Fiber in Node doesn't\n // run them to completion (and exceptions thrown from onException are not\n // propagated), so we need to be in a Fiber.\n Meteor._nodeCodeMustBeInFiber();\n\n const ids = Object.keys(this.callbacks);\n for (let i = 0; i < ids.length; ++i) {\n const id = ids[i];\n // check to see if the callback was removed during iteration\n if (hasOwn.call(this.callbacks, id)) {\n const callback = this.callbacks[id];\n if (! iterator(callback)) {\n break;\n }\n }\n }\n }\n\n /**\n * @deprecated use forEach\n * @param iterator\n */\n each(iterator) {\n return this.forEach(iterator);\n }\n}\n\n// Copied from Meteor.bindEnvironment and removed all the env stuff.\nfunction dontBindEnvironment(func, onException, _this) {\n if (!onException || typeof(onException) === 'string') {\n const description = onException || \"callback of async function\";\n onException = function (error) {\n Meteor._debug(\n \"Exception in \" + description,\n error\n );\n };\n }\n\n return function (...args) {\n let ret;\n try {\n ret = func.apply(_this, args);\n } catch (e) {\n onException(e);\n }\n return ret;\n };\n}\n"]}