You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
9.5 KiB
236 lines
9.5 KiB
4 months ago
|
'use strict';
|
||
|
|
||
|
// This currentContext variable will only be used if the makeSlotClass
|
||
|
// function is called, which happens only if this is the first copy of the
|
||
|
// @wry/context package to be imported.
|
||
|
var currentContext = null;
|
||
|
// This unique internal object is used to denote the absence of a value
|
||
|
// for a given Slot, and is never exposed to outside code.
|
||
|
var MISSING_VALUE = {};
|
||
|
var idCounter = 1;
|
||
|
// Although we can't do anything about the cost of duplicated code from
|
||
|
// accidentally bundling multiple copies of the @wry/context package, we can
|
||
|
// avoid creating the Slot class more than once using makeSlotClass.
|
||
|
var makeSlotClass = function () { return /** @class */ (function () {
|
||
|
function Slot() {
|
||
|
// If you have a Slot object, you can find out its slot.id, but you cannot
|
||
|
// guess the slot.id of a Slot you don't have access to, thanks to the
|
||
|
// randomized suffix.
|
||
|
this.id = [
|
||
|
"slot",
|
||
|
idCounter++,
|
||
|
Date.now(),
|
||
|
Math.random().toString(36).slice(2),
|
||
|
].join(":");
|
||
|
}
|
||
|
Slot.prototype.hasValue = function () {
|
||
|
for (var context_1 = currentContext; context_1; context_1 = context_1.parent) {
|
||
|
// We use the Slot object iself as a key to its value, which means the
|
||
|
// value cannot be obtained without a reference to the Slot object.
|
||
|
if (this.id in context_1.slots) {
|
||
|
var value = context_1.slots[this.id];
|
||
|
if (value === MISSING_VALUE)
|
||
|
break;
|
||
|
if (context_1 !== currentContext) {
|
||
|
// Cache the value in currentContext.slots so the next lookup will
|
||
|
// be faster. This caching is safe because the tree of contexts and
|
||
|
// the values of the slots are logically immutable.
|
||
|
currentContext.slots[this.id] = value;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
if (currentContext) {
|
||
|
// If a value was not found for this Slot, it's never going to be found
|
||
|
// no matter how many times we look it up, so we might as well cache
|
||
|
// the absence of the value, too.
|
||
|
currentContext.slots[this.id] = MISSING_VALUE;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
Slot.prototype.getValue = function () {
|
||
|
if (this.hasValue()) {
|
||
|
return currentContext.slots[this.id];
|
||
|
}
|
||
|
};
|
||
|
Slot.prototype.withValue = function (value, callback,
|
||
|
// Given the prevalence of arrow functions, specifying arguments is likely
|
||
|
// to be much more common than specifying `this`, hence this ordering:
|
||
|
args, thisArg) {
|
||
|
var _a;
|
||
|
var slots = (_a = {
|
||
|
__proto__: null
|
||
|
},
|
||
|
_a[this.id] = value,
|
||
|
_a);
|
||
|
var parent = currentContext;
|
||
|
currentContext = { parent: parent, slots: slots };
|
||
|
try {
|
||
|
// Function.prototype.apply allows the arguments array argument to be
|
||
|
// omitted or undefined, so args! is fine here.
|
||
|
return callback.apply(thisArg, args);
|
||
|
}
|
||
|
finally {
|
||
|
currentContext = parent;
|
||
|
}
|
||
|
};
|
||
|
// Capture the current context and wrap a callback function so that it
|
||
|
// reestablishes the captured context when called.
|
||
|
Slot.bind = function (callback) {
|
||
|
var context = currentContext;
|
||
|
return function () {
|
||
|
var saved = currentContext;
|
||
|
try {
|
||
|
currentContext = context;
|
||
|
return callback.apply(this, arguments);
|
||
|
}
|
||
|
finally {
|
||
|
currentContext = saved;
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
// Immediately run a callback function without any captured context.
|
||
|
Slot.noContext = function (callback,
|
||
|
// Given the prevalence of arrow functions, specifying arguments is likely
|
||
|
// to be much more common than specifying `this`, hence this ordering:
|
||
|
args, thisArg) {
|
||
|
if (currentContext) {
|
||
|
var saved = currentContext;
|
||
|
try {
|
||
|
currentContext = null;
|
||
|
// Function.prototype.apply allows the arguments array argument to be
|
||
|
// omitted or undefined, so args! is fine here.
|
||
|
return callback.apply(thisArg, args);
|
||
|
}
|
||
|
finally {
|
||
|
currentContext = saved;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
return callback.apply(thisArg, args);
|
||
|
}
|
||
|
};
|
||
|
return Slot;
|
||
|
}()); };
|
||
|
function maybe(fn) {
|
||
|
try {
|
||
|
return fn();
|
||
|
}
|
||
|
catch (ignored) { }
|
||
|
}
|
||
|
// We store a single global implementation of the Slot class as a permanent
|
||
|
// non-enumerable property of the globalThis object. This obfuscation does
|
||
|
// nothing to prevent access to the Slot class, but at least it ensures the
|
||
|
// implementation (i.e. currentContext) cannot be tampered with, and all copies
|
||
|
// of the @wry/context package (hopefully just one) will share the same Slot
|
||
|
// implementation. Since the first copy of the @wry/context package to be
|
||
|
// imported wins, this technique imposes a steep cost for any future breaking
|
||
|
// changes to the Slot class.
|
||
|
var globalKey = "@wry/context:Slot";
|
||
|
var host =
|
||
|
// Prefer globalThis when available.
|
||
|
// https://github.com/benjamn/wryware/issues/347
|
||
|
maybe(function () { return globalThis; }) ||
|
||
|
// Fall back to global, which works in Node.js and may be converted by some
|
||
|
// bundlers to the appropriate identifier (window, self, ...) depending on the
|
||
|
// bundling target. https://github.com/endojs/endo/issues/576#issuecomment-1178515224
|
||
|
maybe(function () { return global; }) ||
|
||
|
// Otherwise, use a dummy host that's local to this module. We used to fall
|
||
|
// back to using the Array constructor as a namespace, but that was flagged in
|
||
|
// https://github.com/benjamn/wryware/issues/347, and can be avoided.
|
||
|
Object.create(null);
|
||
|
// Whichever globalHost we're using, make TypeScript happy about the additional
|
||
|
// globalKey property.
|
||
|
var globalHost = host;
|
||
|
var Slot = globalHost[globalKey] ||
|
||
|
// Earlier versions of this package stored the globalKey property on the Array
|
||
|
// constructor, so we check there as well, to prevent Slot class duplication.
|
||
|
Array[globalKey] ||
|
||
|
(function (Slot) {
|
||
|
try {
|
||
|
Object.defineProperty(globalHost, globalKey, {
|
||
|
value: Slot,
|
||
|
enumerable: false,
|
||
|
writable: false,
|
||
|
// When it was possible for globalHost to be the Array constructor (a
|
||
|
// legacy Slot dedup strategy), it was important for the property to be
|
||
|
// configurable:true so it could be deleted. That does not seem to be as
|
||
|
// important when globalHost is the global object, but I don't want to
|
||
|
// cause similar problems again, and configurable:true seems safest.
|
||
|
// https://github.com/endojs/endo/issues/576#issuecomment-1178274008
|
||
|
configurable: true
|
||
|
});
|
||
|
}
|
||
|
finally {
|
||
|
return Slot;
|
||
|
}
|
||
|
})(makeSlotClass());
|
||
|
|
||
|
var bind = Slot.bind, noContext = Slot.noContext;
|
||
|
function setTimeoutWithContext(callback, delay) {
|
||
|
return setTimeout(bind(callback), delay);
|
||
|
}
|
||
|
// Turn any generator function into an async function (using yield instead
|
||
|
// of await), with context automatically preserved across yields.
|
||
|
function asyncFromGen(genFn) {
|
||
|
return function () {
|
||
|
var gen = genFn.apply(this, arguments);
|
||
|
var boundNext = bind(gen.next);
|
||
|
var boundThrow = bind(gen.throw);
|
||
|
return new Promise(function (resolve, reject) {
|
||
|
function invoke(method, argument) {
|
||
|
try {
|
||
|
var result = method.call(gen, argument);
|
||
|
}
|
||
|
catch (error) {
|
||
|
return reject(error);
|
||
|
}
|
||
|
var next = result.done ? resolve : invokeNext;
|
||
|
if (isPromiseLike(result.value)) {
|
||
|
result.value.then(next, result.done ? reject : invokeThrow);
|
||
|
}
|
||
|
else {
|
||
|
next(result.value);
|
||
|
}
|
||
|
}
|
||
|
var invokeNext = function (value) { return invoke(boundNext, value); };
|
||
|
var invokeThrow = function (error) { return invoke(boundThrow, error); };
|
||
|
invokeNext();
|
||
|
});
|
||
|
};
|
||
|
}
|
||
|
function isPromiseLike(value) {
|
||
|
return value && typeof value.then === "function";
|
||
|
}
|
||
|
// If you use the fibers npm package to implement coroutines in Node.js,
|
||
|
// you should call this function at least once to ensure context management
|
||
|
// remains coherent across any yields.
|
||
|
var wrappedFibers = [];
|
||
|
function wrapYieldingFiberMethods(Fiber) {
|
||
|
// There can be only one implementation of Fiber per process, so this array
|
||
|
// should never grow longer than one element.
|
||
|
if (wrappedFibers.indexOf(Fiber) < 0) {
|
||
|
var wrap = function (obj, method) {
|
||
|
var fn = obj[method];
|
||
|
obj[method] = function () {
|
||
|
return noContext(fn, arguments, this);
|
||
|
};
|
||
|
};
|
||
|
// These methods can yield, according to
|
||
|
// https://github.com/laverdet/node-fibers/blob/ddebed9b8ae3883e57f822e2108e6943e5c8d2a8/fibers.js#L97-L100
|
||
|
wrap(Fiber, "yield");
|
||
|
wrap(Fiber.prototype, "run");
|
||
|
wrap(Fiber.prototype, "throwInto");
|
||
|
wrappedFibers.push(Fiber);
|
||
|
}
|
||
|
return Fiber;
|
||
|
}
|
||
|
|
||
|
exports.Slot = Slot;
|
||
|
exports.asyncFromGen = asyncFromGen;
|
||
|
exports.bind = bind;
|
||
|
exports.noContext = noContext;
|
||
|
exports.setTimeout = setTimeoutWithContext;
|
||
|
exports.wrapYieldingFiberMethods = wrapYieldingFiberMethods;
|
||
|
//# sourceMappingURL=bundle.cjs.map
|