@wry/context
Advanced tools
Comparing version 0.3.2 to 0.4.0
/// <reference types="node" /> | ||
export declare class Slot<TValue> { | ||
private readonly id; | ||
hasValue(): boolean; | ||
getValue(): TValue | undefined; | ||
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, TArgs extends any[], TThis = any>(callback: (this: TThis, ...args: TArgs) => TResult, args?: TArgs, thisArg?: TThis): TResult; | ||
import { Slot } from "./slot"; | ||
export { Slot }; | ||
export declare const bind: <TArgs extends any[], TResult>(callback: (...args: TArgs) => TResult) => (...args: TArgs) => TResult, noContext: <TResult, TArgs extends any[], TThis = any>(callback: (this: TThis, ...args: TArgs) => TResult, args?: TArgs | undefined, thisArg?: TThis | undefined) => TResult; | ||
export { setTimeoutWithContext as setTimeout }; | ||
@@ -11,0 +6,0 @@ declare function setTimeoutWithContext(callback: () => any, delay: number): NodeJS.Timeout; |
@@ -0,1 +1,4 @@ | ||
// 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; | ||
@@ -6,7 +9,10 @@ // This unique internal object is used to denote the absence of a value | ||
var idCounter = 1; | ||
var Slot = /** @class */ (function () { | ||
// 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 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. | ||
// 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 = [ | ||
@@ -20,8 +26,7 @@ "slot", | ||
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 (this.id in context_1.slots) { | ||
var value = context_1.slots[this.id]; | ||
if (value === MISSING_VALUE) | ||
@@ -33,3 +38,3 @@ break; | ||
// the values of the slots are logically immutable. | ||
currentContext.slots[id] = value; | ||
currentContext.slots[this.id] = value; | ||
} | ||
@@ -43,3 +48,3 @@ return true; | ||
// the absence of the value, too. | ||
currentContext.slots[id] = MISSING_VALUE; | ||
currentContext.slots[this.id] = MISSING_VALUE; | ||
} | ||
@@ -74,46 +79,66 @@ return false; | ||
}; | ||
return Slot; | ||
}()); | ||
var boundBrand = Symbol(); | ||
// Capture the current context and wrap a callback function so that it | ||
// reestablishes the captured context when called. | ||
function bind(callback) { | ||
if (callback[boundBrand] === bind) { | ||
return callback; | ||
} | ||
var context = currentContext; | ||
var bound = function () { | ||
var saved = currentContext; | ||
try { | ||
currentContext = context; | ||
return callback.apply(this, arguments); | ||
// 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; | ||
} | ||
} | ||
finally { | ||
currentContext = saved; | ||
else { | ||
return callback.apply(thisArg, args); | ||
} | ||
}; | ||
bound[boundBrand] = bind; | ||
return bound; | ||
} | ||
// Immediately run a callback function without any captured context. | ||
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) { | ||
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; | ||
} | ||
return Slot; | ||
}()); }; | ||
// We store a single global implementation of the Slot class as a permanent | ||
// non-enumerable symbol property of the Array constructor. 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 very high cost for any | ||
// future breaking changes to the Slot class. | ||
var globalKey = "@wry/context:Slot"; | ||
var host = Array; | ||
var Slot = host[globalKey] || function () { | ||
var Slot = makeSlotClass(); | ||
try { | ||
Object.defineProperty(host, globalKey, { | ||
value: host[globalKey] = Slot, | ||
enumerable: false, | ||
writable: false, | ||
configurable: false, | ||
}); | ||
} | ||
else { | ||
return callback.apply(thisArg, args); | ||
finally { | ||
return Slot; | ||
} | ||
} | ||
}(); | ||
var bind = Slot.bind, noContext = Slot.noContext; | ||
function setTimeoutWithContext(callback, delay) { | ||
@@ -120,0 +145,0 @@ return setTimeout(bind(callback), delay); |
@@ -5,2 +5,5 @@ '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; | ||
@@ -11,7 +14,10 @@ // This unique internal object is used to denote the absence of a value | ||
var idCounter = 1; | ||
var Slot = /** @class */ (function () { | ||
// 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 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. | ||
// 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 = [ | ||
@@ -25,8 +31,7 @@ "slot", | ||
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 (this.id in context_1.slots) { | ||
var value = context_1.slots[this.id]; | ||
if (value === MISSING_VALUE) | ||
@@ -38,3 +43,3 @@ break; | ||
// the values of the slots are logically immutable. | ||
currentContext.slots[id] = value; | ||
currentContext.slots[this.id] = value; | ||
} | ||
@@ -48,3 +53,3 @@ return true; | ||
// the absence of the value, too. | ||
currentContext.slots[id] = MISSING_VALUE; | ||
currentContext.slots[this.id] = MISSING_VALUE; | ||
} | ||
@@ -79,46 +84,66 @@ return false; | ||
}; | ||
return Slot; | ||
}()); | ||
var boundBrand = Symbol(); | ||
// Capture the current context and wrap a callback function so that it | ||
// reestablishes the captured context when called. | ||
function bind(callback) { | ||
if (callback[boundBrand] === bind) { | ||
return callback; | ||
} | ||
var context = currentContext; | ||
var bound = function () { | ||
var saved = currentContext; | ||
try { | ||
currentContext = context; | ||
return callback.apply(this, arguments); | ||
// 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; | ||
} | ||
} | ||
finally { | ||
currentContext = saved; | ||
else { | ||
return callback.apply(thisArg, args); | ||
} | ||
}; | ||
bound[boundBrand] = bind; | ||
return bound; | ||
} | ||
// Immediately run a callback function without any captured context. | ||
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) { | ||
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; | ||
} | ||
return Slot; | ||
}()); }; | ||
// We store a single global implementation of the Slot class as a permanent | ||
// non-enumerable symbol property of the Array constructor. 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 very high cost for any | ||
// future breaking changes to the Slot class. | ||
var globalKey = "@wry/context:Slot"; | ||
var host = Array; | ||
var Slot = host[globalKey] || function () { | ||
var Slot = makeSlotClass(); | ||
try { | ||
Object.defineProperty(host, globalKey, { | ||
value: host[globalKey] = Slot, | ||
enumerable: false, | ||
writable: false, | ||
configurable: false, | ||
}); | ||
} | ||
else { | ||
return callback.apply(thisArg, args); | ||
finally { | ||
return Slot; | ||
} | ||
} | ||
}(); | ||
var bind = Slot.bind, noContext = Slot.noContext; | ||
function setTimeoutWithContext(callback, delay) { | ||
@@ -125,0 +150,0 @@ return setTimeout(bind(callback), delay); |
{ | ||
"name": "@wry/context", | ||
"version": "0.3.2", | ||
"version": "0.4.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": "c619f94dcebed8f4dbb0581997f890c42a2de192" | ||
"gitHead": "b08b9b86751f97f972df48b847da2a68fbdc6801" | ||
} |
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
31701
13
608