@wry/context
Advanced tools
Comparing version 0.1.5 to 0.2.0
/// <reference types="node" /> | ||
export declare class Slot<TValue> { | ||
constructor(); | ||
hasValue: () => boolean; | ||
private readonly id; | ||
hasValue(): boolean; | ||
getValue: () => TValue | undefined; | ||
withValue: <TResult>(value: TValue, callback: () => TResult) => TResult; | ||
withValue<TResult, TArgs extends any[], TThis = any>(value: TValue, callback: (this: TThis, ...args: TArgs) => TResult, args?: TArgs, thisArg?: TThis): TResult; | ||
} | ||
export declare function bind<TArgs extends any[], TResult>(callback: (...args: TArgs) => TResult): (...args: TArgs) => TResult; | ||
export declare function noContext<TResult>(callback: () => TResult): TResult; | ||
export declare function noContext<TResult, TArgs extends any[], TThis = any>(callback: (this: TThis, ...args: TArgs) => TResult, args?: TArgs, thisArg?: TThis): TResult; | ||
export { setTimeoutWithContext as setTimeout }; | ||
declare function setTimeoutWithContext(callback: () => any, delay: number): NodeJS.Timeout; | ||
export declare function asyncFromGen<TArgs extends any[], TResult>(genFn: (...args: TArgs) => IterableIterator<TResult>): (...args: TArgs) => Promise<TResult>; |
var currentContext = null; | ||
var slotIdMap = new WeakMap(); | ||
// Pull down the prototype methods that we use onto the slotIdMap instance | ||
// so that they can't be tampered with by malicious code. | ||
slotIdMap.set = slotIdMap.set; | ||
slotIdMap.get = slotIdMap.get; | ||
var nextSlotId = 1; | ||
// 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 = {}; | ||
// Returns the ID of the given slot if the slot has a value defined. | ||
// Caches the result in currentContext.slots for faster future lookups. | ||
function lookup(slot) { | ||
var slotId = slotIdMap.get(slot); | ||
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 (slotId in context_1.slots) { | ||
var value = context_1.slots[slotId]; | ||
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[slotId] = value; | ||
} | ||
return slotId; | ||
} | ||
} | ||
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[slotId] = MISSING_VALUE; | ||
} | ||
var idCounter = 1; | ||
function makeUniqueId() { | ||
return ["slot", idCounter++, Date.now(), Math.random().toString(36).slice(2)].join(":"); | ||
} | ||
@@ -41,26 +12,58 @@ var Slot = /** @class */ (function () { | ||
var _this = this; | ||
this.hasValue = function () { return !!lookup(_this); }; | ||
// If you have a Slot object, you can find out its slot.id by circumventing | ||
// TypeScript's privacy restrictions, but you can't guess the slot.id of a | ||
// Slot you don't have access to, thanks to the randomized suffix. | ||
this.id = makeUniqueId(); | ||
this.getValue = function () { | ||
var slotId = lookup(_this); | ||
if (slotId) { | ||
return currentContext.slots[slotId]; | ||
if (_this.hasValue()) { | ||
return currentContext.slots[_this.id]; | ||
} | ||
}; | ||
this.withValue = function (value, callback) { | ||
var _a; | ||
var slots = (_a = { | ||
__proto__: null | ||
}, | ||
_a[slotIdMap.get(_this)] = value, | ||
_a); | ||
currentContext = { parent: currentContext, slots: slots }; | ||
try { | ||
return callback(); | ||
} | ||
Slot.prototype.hasValue = function () { | ||
var id = this.id; | ||
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 (id in context_1.slots) { | ||
var value = context_1.slots[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[id] = value; | ||
} | ||
return true; | ||
} | ||
finally { | ||
currentContext = currentContext.parent; | ||
} | ||
}; | ||
slotIdMap.set(this, nextSlotId++); | ||
} | ||
} | ||
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[id] = MISSING_VALUE; | ||
} | ||
return false; | ||
}; | ||
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); | ||
currentContext = { parent: currentContext, 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 = currentContext.parent; | ||
} | ||
}; | ||
return Slot; | ||
@@ -90,7 +93,12 @@ }()); | ||
// Immediately run a callback function without any captured context. | ||
function noContext(callback) { | ||
function noContext(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 saved = currentContext; | ||
try { | ||
currentContext = null; | ||
return callback(); | ||
// Function.prototype.apply allows the arguments array argument to be | ||
// omitted or undefined, so args! is fine here. | ||
return callback.apply(thisArg, args); | ||
} | ||
@@ -97,0 +105,0 @@ finally { |
@@ -6,37 +6,8 @@ 'use strict'; | ||
var currentContext = null; | ||
var slotIdMap = new WeakMap(); | ||
// Pull down the prototype methods that we use onto the slotIdMap instance | ||
// so that they can't be tampered with by malicious code. | ||
slotIdMap.set = slotIdMap.set; | ||
slotIdMap.get = slotIdMap.get; | ||
var nextSlotId = 1; | ||
// 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 = {}; | ||
// Returns the ID of the given slot if the slot has a value defined. | ||
// Caches the result in currentContext.slots for faster future lookups. | ||
function lookup(slot) { | ||
var slotId = slotIdMap.get(slot); | ||
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 (slotId in context_1.slots) { | ||
var value = context_1.slots[slotId]; | ||
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[slotId] = value; | ||
} | ||
return slotId; | ||
} | ||
} | ||
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[slotId] = MISSING_VALUE; | ||
} | ||
var idCounter = 1; | ||
function makeUniqueId() { | ||
return ["slot", idCounter++, Date.now(), Math.random().toString(36).slice(2)].join(":"); | ||
} | ||
@@ -46,26 +17,58 @@ var Slot = /** @class */ (function () { | ||
var _this = this; | ||
this.hasValue = function () { return !!lookup(_this); }; | ||
// If you have a Slot object, you can find out its slot.id by circumventing | ||
// TypeScript's privacy restrictions, but you can't guess the slot.id of a | ||
// Slot you don't have access to, thanks to the randomized suffix. | ||
this.id = makeUniqueId(); | ||
this.getValue = function () { | ||
var slotId = lookup(_this); | ||
if (slotId) { | ||
return currentContext.slots[slotId]; | ||
if (_this.hasValue()) { | ||
return currentContext.slots[_this.id]; | ||
} | ||
}; | ||
this.withValue = function (value, callback) { | ||
var _a; | ||
var slots = (_a = { | ||
__proto__: null | ||
}, | ||
_a[slotIdMap.get(_this)] = value, | ||
_a); | ||
currentContext = { parent: currentContext, slots: slots }; | ||
try { | ||
return callback(); | ||
} | ||
Slot.prototype.hasValue = function () { | ||
var id = this.id; | ||
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 (id in context_1.slots) { | ||
var value = context_1.slots[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[id] = value; | ||
} | ||
return true; | ||
} | ||
finally { | ||
currentContext = currentContext.parent; | ||
} | ||
}; | ||
slotIdMap.set(this, nextSlotId++); | ||
} | ||
} | ||
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[id] = MISSING_VALUE; | ||
} | ||
return false; | ||
}; | ||
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); | ||
currentContext = { parent: currentContext, 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 = currentContext.parent; | ||
} | ||
}; | ||
return Slot; | ||
@@ -95,7 +98,12 @@ }()); | ||
// Immediately run a callback function without any captured context. | ||
function noContext(callback) { | ||
function noContext(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 saved = currentContext; | ||
try { | ||
currentContext = null; | ||
return callback(); | ||
// Function.prototype.apply allows the arguments array argument to be | ||
// omitted or undefined, so args! is fine here. | ||
return callback.apply(thisArg, args); | ||
} | ||
@@ -102,0 +110,0 @@ finally { |
{ | ||
"name": "@wry/context", | ||
"version": "0.1.5", | ||
"version": "0.2.0", | ||
"author": "Ben Newman <ben@eloper.dev>", | ||
@@ -31,3 +31,3 @@ "description": "Manage contextual information needed by (a)synchronous tasks without explicitly passing objects around", | ||
}, | ||
"gitHead": "704d10be24c3468c625257312a18a54c0d09d5a9" | ||
"gitHead": "ac45fec8d46eedc1518265d9141266d352278fb1" | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18313
346