UNPKG

disposable-cls

Version:

Provides disposable continuation local storage for Node.js.

217 lines (216 loc) 8.49 kB
"use strict"; var shimmer_1 = require("shimmer"); var ContextStackItem_1 = require("./ContextStackItem"); /** * Reacts to Node.js asynchronous scheduling events and manages a scope stack that will * be preserved across those asynchronous invocations. */ var ContextStack = (function () { /** * Initializes a new ContextStack. */ function ContextStack() { ContextStack.activeContext = null; } Object.defineProperty(ContextStack, "activeContext", { /** * Gets the current context stack item. * * @returns The currently active context stack item. */ get: function () { return process[ContextStack.ACTIVECONTEXT_PROCESS_SLOTNAME]; }, /** * Sets the current context stack item. * * @param value The context stack item to make current. */ set: function (value) { process[ContextStack.ACTIVECONTEXT_PROCESS_SLOTNAME] = value; }, enumerable: true, configurable: true }); /** * Called by Node.js when an asynchronous function is being scheduled. * * The contextual items pushed onto the managed scope stack are captured and maintained * throughout the lifetime of the asynchronous function being scheduled. */ ContextStack.prototype.create = function () { return new ContextStackItem_1.ContextStackItem(ContextStack.scopeStack.pop(), ContextStack.captureStackItem(ContextStack.activeContext)); }; /** * Called by Node.js before an asynchronous function is to be executed. * * @param context Represents the current 'this' object. * @param contextStackItem The context stack item that was created for the asynchronous * function. */ ContextStack.prototype.before = function (context, contextStackItem) { ContextStack.activeContext = contextStackItem; }; /** * Called by Node.js after an asynchronous function has executed. * * @param context Represents the current 'this' object. * @param contextStackItem The context stack item that was created for the asynchronous * function. */ ContextStack.prototype.after = function (context, contextStackItem) { ContextStack.activeContext = ContextStack.releaseStackItem(contextStackItem); }; /** * Called by Node.js if an asynchronous function has thrown an error. * * @param contextStackItem The context stack item that was created for the asynchronous * function. * @param error The error that was thrown. */ ContextStack.prototype.error = function (contextStackItem, error) { ContextStack.activeContext = ContextStack.releaseStackItem(contextStackItem); }; /** * Pushes contextual items onto the managed scope stack so that they can be captured as part * of a context stack item when the next asynchronous function is being scheduled. * * @param contextItems The array of objects to capture. */ ContextStack.prototype.pushScope = function (contextItems) { var scopeStackItem = {}; contextItems.forEach(function (contextItem) { scopeStackItem[contextItem.constructor.name] = contextItem; }); ContextStack.scopeStack.push(scopeStackItem); }; /** * Searches the current context stack for a contextual item that was captured for the given * type of object. * * @param objectType The constructor function of the object that is required. * @returns The object that is currently in scope, or 'undefined' if none was found. */ ContextStack.prototype.findContextObjectFromScope = function (objectType) { var objectTypeName = objectType.name; var context = ContextStack.activeContext; var object = undefined; while (context) { object = context.data ? context.data[objectTypeName] : undefined; if (object) { return object; } context = context.parent; } return undefined; }; /** * Bind an EventEmitter to the currently active context stack. * * @param emitter The EventEmitter to bind. */ ContextStack.prototype.bindEventEmitter = function (emitter) { var ACTIVECONTEXT_LISTENER_SLOTNAME = "__cls_capturedcontext"; var onAddListener = function (originalAddListenerFunc) { return function (event, listener) { listener[ACTIVECONTEXT_LISTENER_SLOTNAME] = ContextStack.captureStackItem(ContextStack.activeContext); return originalAddListenerFunc.apply(this, arguments); }; }; var onEmit = function (originalEmitFunc) { return function (event) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } if (!this._events || !this._events[event]) { return originalEmitFunc.apply(this, arguments); } var listener = this._events[event]; var currentContext = ContextStack.activeContext; try { ContextStack.activeContext = listener[ACTIVECONTEXT_LISTENER_SLOTNAME]; return originalEmitFunc.apply(this, arguments); } finally { ContextStack.activeContext = currentContext; } }; }; var onRemoveListener = function (originalRemoveListenerFunc) { return function (event, listener) { ContextStack.releaseStackItem(listener[ACTIVECONTEXT_LISTENER_SLOTNAME]); return originalRemoveListenerFunc.apply(this, arguments); }; }; shimmer_1.wrap(emitter, "addListener", onAddListener); shimmer_1.wrap(emitter, "on", onAddListener); shimmer_1.wrap(emitter, "emit", onEmit); shimmer_1.wrap(emitter, "removeListener", onRemoveListener); }; /** * Resets the managed scope stack. * * @returns A boolean flag indicating if the ContextStack could be reset. */ ContextStack.tryReset = function () { if (ContextStack.activeContext) { return false; } ContextStack.activeContext = null; ContextStack.scopeStack.splice(0, ContextStack.scopeStack.length); return true; }; /** * Captures a given context stack item by incrementing the reference counts across * the entire stack. * * @param contextStackItem The context stack item that is to be captured. * @returns The original context stack item. */ ContextStack.captureStackItem = function (contextStackItem) { var context = contextStackItem; while (context) { context.addRef(); context = context.parent; } return contextStackItem; }; /** * Releases a given context stack item. * * @param contextStackItem The context stack item that is to be released. * @returns The parent context stack item. */ ContextStack.releaseStackItem = function (contextStackItem) { if (!contextStackItem) { return null; // nothing to release } var context = contextStackItem; while (context) { if (context.release() === 0) { ContextStack.disposeStackItemObjects(context.data); } context = context.parent; } return contextStackItem.parent; }; /** * Disposes of objects in a given object by calling their 'dispose()' function if they * have one. * * @param data An object that will be enumerated. */ ContextStack.disposeStackItemObjects = function (data) { for (var key in data) { var object = data[key]; if (object["dispose"]) { object.dispose(); } } }; ContextStack.ACTIVECONTEXT_PROCESS_SLOTNAME = "__cls_activecontext"; ContextStack.scopeStack = []; return ContextStack; }()); exports.ContextStack = ContextStack;