Outlook_Addin_LLM/node_modules/cls-hooked/context-legacy.js

421 lines
11 KiB
JavaScript

'use strict';
const util = require('util');
const assert = require('assert');
const wrapEmitter = require('emitter-listener');
const asyncHook = require('async-hook-jl');
const CONTEXTS_SYMBOL = 'cls@contexts';
const ERROR_SYMBOL = 'error@context';
//const trace = [];
const invertedProviders = [];
for (let key in asyncHook.providers) {
invertedProviders[asyncHook.providers[key]] = key;
}
const DEBUG_CLS_HOOKED = process.env.DEBUG_CLS_HOOKED;
let currentUid = -1;
module.exports = {
getNamespace: getNamespace,
createNamespace: createNamespace,
destroyNamespace: destroyNamespace,
reset: reset,
//trace: trace,
ERROR_SYMBOL: ERROR_SYMBOL
};
function Namespace(name) {
this.name = name;
// changed in 2.7: no default context
this.active = null;
this._set = [];
this.id = null;
this._contexts = new Map();
}
Namespace.prototype.set = function set(key, value) {
if (!this.active) {
throw new Error('No context available. ns.run() or ns.bind() must be called first.');
}
if (DEBUG_CLS_HOOKED) {
debug2(' SETTING KEY:' + key + '=' + value + ' in ns:' + this.name + ' uid:' + currentUid + ' active:' +
util.inspect(this.active, true));
}
this.active[key] = value;
return value;
};
Namespace.prototype.get = function get(key) {
if (!this.active) {
if (DEBUG_CLS_HOOKED) {
debug2(' GETTING KEY:' + key + '=undefined' + ' ' + this.name + ' uid:' + currentUid + ' active:' +
util.inspect(this.active, true));
}
return undefined;
}
if (DEBUG_CLS_HOOKED) {
debug2(' GETTING KEY:' + key + '=' + this.active[key] + ' ' + this.name + ' uid:' + currentUid + ' active:' +
util.inspect(this.active, true));
}
return this.active[key];
};
Namespace.prototype.createContext = function createContext() {
if (DEBUG_CLS_HOOKED) {
debug2(' CREATING Context: ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' ' + ' active:' +
util.inspect(this.active, true, 2, true));
}
let context = Object.create(this.active ? this.active : Object.prototype);
context._ns_name = this.name;
context.id = currentUid;
if (DEBUG_CLS_HOOKED) {
debug2(' CREATED Context: ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' ' + ' context:' +
util.inspect(context, true, 2, true));
}
return context;
};
Namespace.prototype.run = function run(fn) {
let context = this.createContext();
this.enter(context);
try {
if (DEBUG_CLS_HOOKED) {
debug2(' BEFORE RUN: ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' ' +
util.inspect(context));
}
fn(context);
return context;
}
catch (exception) {
if (exception) {
exception[ERROR_SYMBOL] = context;
}
throw exception;
}
finally {
if (DEBUG_CLS_HOOKED) {
debug2(' AFTER RUN: ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' ' +
util.inspect(context));
}
this.exit(context);
}
};
Namespace.prototype.runAndReturn = function runAndReturn(fn) {
var value;
this.run(function (context) {
value = fn(context);
});
return value;
};
/**
* Uses global Promise and assumes Promise is cls friendly or wrapped already.
* @param {function} fn
* @returns {*}
*/
Namespace.prototype.runPromise = function runPromise(fn) {
let context = this.createContext();
this.enter(context);
let promise = fn(context);
if (!promise || !promise.then || !promise.catch) {
throw new Error('fn must return a promise.');
}
if (DEBUG_CLS_HOOKED) {
debug2(' BEFORE runPromise: ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' ' +
util.inspect(context));
}
return promise
.then(result => {
if (DEBUG_CLS_HOOKED) {
debug2(' AFTER runPromise: ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' ' +
util.inspect(context));
}
this.exit(context);
return result;
})
.catch(err => {
err[ERROR_SYMBOL] = context;
if (DEBUG_CLS_HOOKED) {
debug2(' AFTER runPromise: ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' ' +
util.inspect(context));
}
this.exit(context);
throw err;
});
};
Namespace.prototype.bind = function bindFactory(fn, context) {
if (!context) {
if (!this.active) {
context = this.createContext();
}
else {
context = this.active;
}
}
let self = this;
return function clsBind() {
self.enter(context);
try {
return fn.apply(this, arguments);
}
catch (exception) {
if (exception) {
exception[ERROR_SYMBOL] = context;
}
throw exception;
}
finally {
self.exit(context);
}
};
};
Namespace.prototype.enter = function enter(context) {
assert.ok(context, 'context must be provided for entering');
if (DEBUG_CLS_HOOKED) {
debug2(' ENTER ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' context: ' +
util.inspect(context));
}
this._set.push(this.active);
this.active = context;
};
Namespace.prototype.exit = function exit(context) {
assert.ok(context, 'context must be provided for exiting');
if (DEBUG_CLS_HOOKED) {
debug2(' EXIT ' + this.name + ' uid:' + currentUid + ' len:' + this._set.length + ' context: ' +
util.inspect(context));
}
// Fast path for most exits that are at the top of the stack
if (this.active === context) {
assert.ok(this._set.length, 'can\'t remove top context');
this.active = this._set.pop();
return;
}
// Fast search in the stack using lastIndexOf
let index = this._set.lastIndexOf(context);
if (index < 0) {
if (DEBUG_CLS_HOOKED) {
debug2('??ERROR?? context exiting but not entered - ignoring: ' + util.inspect(context));
}
assert.ok(index >= 0, 'context not currently entered; can\'t exit. \n' + util.inspect(this) + '\n' +
util.inspect(context));
} else {
assert.ok(index, 'can\'t remove top context');
this._set.splice(index, 1);
}
};
Namespace.prototype.bindEmitter = function bindEmitter(emitter) {
assert.ok(emitter.on && emitter.addListener && emitter.emit, 'can only bind real EEs');
let namespace = this;
let thisSymbol = 'context@' + this.name;
// Capture the context active at the time the emitter is bound.
function attach(listener) {
if (!listener) {
return;
}
if (!listener[CONTEXTS_SYMBOL]) {
listener[CONTEXTS_SYMBOL] = Object.create(null);
}
listener[CONTEXTS_SYMBOL][thisSymbol] = {
namespace: namespace,
context: namespace.active
};
}
// At emit time, bind the listener within the correct context.
function bind(unwrapped) {
if (!(unwrapped && unwrapped[CONTEXTS_SYMBOL])) {
return unwrapped;
}
let wrapped = unwrapped;
let unwrappedContexts = unwrapped[CONTEXTS_SYMBOL];
Object.keys(unwrappedContexts).forEach(function (name) {
let thunk = unwrappedContexts[name];
wrapped = thunk.namespace.bind(wrapped, thunk.context);
});
return wrapped;
}
wrapEmitter(emitter, attach, bind);
};
/**
* If an error comes out of a namespace, it will have a context attached to it.
* This function knows how to find it.
*
* @param {Error} exception Possibly annotated error.
*/
Namespace.prototype.fromException = function fromException(exception) {
return exception[ERROR_SYMBOL];
};
function getNamespace(name) {
return process.namespaces[name];
}
function createNamespace(name) {
assert.ok(name, 'namespace must be given a name.');
if (DEBUG_CLS_HOOKED) {
debug2('CREATING NAMESPACE ' + name);
}
let namespace = new Namespace(name);
namespace.id = currentUid;
asyncHook.addHooks({
init(uid, handle, provider, parentUid, parentHandle) {
//parentUid = parentUid || currentUid; // Suggested usage but appears to work better for tracing modules.
currentUid = uid;
//CHAIN Parent's Context onto child if none exists. This is needed to pass net-events.spec
if (parentUid) {
namespace._contexts.set(uid, namespace._contexts.get(parentUid));
if (DEBUG_CLS_HOOKED) {
debug2('PARENTID: ' + name + ' uid:' + uid + ' parent:' + parentUid + ' provider:' + provider);
}
} else {
namespace._contexts.set(currentUid, namespace.active);
}
if (DEBUG_CLS_HOOKED) {
debug2('INIT ' + name + ' uid:' + uid + ' parent:' + parentUid + ' provider:' + invertedProviders[provider]
+ ' active:' + util.inspect(namespace.active, true));
}
},
pre(uid, handle) {
currentUid = uid;
let context = namespace._contexts.get(uid);
if (context) {
if (DEBUG_CLS_HOOKED) {
debug2(' PRE ' + name + ' uid:' + uid + ' handle:' + getFunctionName(handle) + ' context:' +
util.inspect(context));
}
namespace.enter(context);
} else {
if (DEBUG_CLS_HOOKED) {
debug2(' PRE MISSING CONTEXT ' + name + ' uid:' + uid + ' handle:' + getFunctionName(handle));
}
}
},
post(uid, handle) {
currentUid = uid;
let context = namespace._contexts.get(uid);
if (context) {
if (DEBUG_CLS_HOOKED) {
debug2(' POST ' + name + ' uid:' + uid + ' handle:' + getFunctionName(handle) + ' context:' +
util.inspect(context));
}
namespace.exit(context);
} else {
if (DEBUG_CLS_HOOKED) {
debug2(' POST MISSING CONTEXT ' + name + ' uid:' + uid + ' handle:' + getFunctionName(handle));
}
}
},
destroy(uid) {
currentUid = uid;
if (DEBUG_CLS_HOOKED) {
debug2('DESTROY ' + name + ' uid:' + uid + ' context:' + util.inspect(namespace._contexts.get(currentUid))
+ ' active:' + util.inspect(namespace.active, true));
}
namespace._contexts.delete(uid);
}
});
process.namespaces[name] = namespace;
return namespace;
}
function destroyNamespace(name) {
let namespace = getNamespace(name);
assert.ok(namespace, 'can\'t delete nonexistent namespace! "' + name + '"');
assert.ok(namespace.id, 'don\'t assign to process.namespaces directly! ' + util.inspect(namespace));
process.namespaces[name] = null;
}
function reset() {
// must unregister async listeners
if (process.namespaces) {
Object.keys(process.namespaces).forEach(function (name) {
destroyNamespace(name);
});
}
process.namespaces = Object.create(null);
}
process.namespaces = {};
if (asyncHook._state && !asyncHook._state.enabled) {
asyncHook.enable();
}
function debug2(msg) {
if (process.env.DEBUG) {
process._rawDebug(msg);
}
}
/*function debug(from, ns) {
process._rawDebug('DEBUG: ' + util.inspect({
from: from,
currentUid: currentUid,
context: ns ? ns._contexts.get(currentUid) : 'no ns'
}, true, 2, true));
}*/
function getFunctionName(fn) {
if (!fn) {
return fn;
}
if (typeof fn === 'function') {
if (fn.name) {
return fn.name;
}
return (fn.toString().trim().match(/^function\s*([^\s(]+)/) || [])[1];
} else if (fn.constructor && fn.constructor.name) {
return fn.constructor.name;
}
}
// Add back to callstack
if (DEBUG_CLS_HOOKED) {
var stackChain = require('stack-chain');
for (var modifier in stackChain.filter._modifiers) {
stackChain.filter.deattach(modifier);
}
}