@agoric/eventual-send
Advanced tools
Comparing version 0.5.0 to 0.5.1
@@ -69,3 +69,3 @@ 'use strict'; | ||
get(_target, prop) { | ||
return HandledPromise.get(x, prop); | ||
return harden(HandledPromise.get(x, prop)); | ||
}, | ||
@@ -76,2 +76,3 @@ }); | ||
E.resolve = HandledPromise.resolve; | ||
E.unwrap = HandledPromise.unwrap; | ||
@@ -83,4 +84,13 @@ return harden(E); | ||
const { defineProperties, getOwnPropertyDescriptors } = Object; | ||
const { | ||
defineProperties, | ||
getOwnPropertyDescriptors, | ||
getOwnPropertyDescriptor: gopd, | ||
getPrototypeOf, | ||
isFrozen, | ||
} = Object; | ||
const { prototype: promiseProto } = Promise; | ||
const { then: originalThen } = promiseProto; | ||
// 'E' and 'HandledPromise' are exports of the module | ||
@@ -121,2 +131,3 @@ | ||
let promiseToHandler; | ||
let promiseToPresence; // only for HandledPromise.unwrap | ||
function ensureMaps() { | ||
@@ -127,2 +138,3 @@ if (!presenceToHandler) { | ||
promiseToHandler = new WeakMap(); | ||
promiseToPresence = new WeakMap(); | ||
} | ||
@@ -135,3 +147,3 @@ } | ||
let handle; | ||
let handledPromiseResolve; | ||
let promiseResolve; | ||
@@ -180,2 +192,5 @@ function HandledPromise(executor, unfulfilledHandler = undefined) { | ||
}); | ||
// A failed interlock should not be recorded as an unhandled rejection. | ||
// It will bubble up to the HandledPromise itself. | ||
interlockP.catch(_ => {}); | ||
@@ -239,2 +254,3 @@ const makePostponed = postponedOperation => { | ||
presenceToPromise.set(resolvedPresence, handledP); | ||
promiseToPresence.set(handledP, resolvedPresence); | ||
presenceToHandler.set(resolvedPresence, presenceHandler); | ||
@@ -279,6 +295,12 @@ | ||
// See if the target is a presence we already know of. | ||
const presence = await target; | ||
let presence; | ||
try { | ||
presence = HandledPromise.unwrap(target); | ||
} catch (e) { | ||
presence = await target; | ||
} | ||
const existingPresenceHandler = presenceToHandler.get(presence); | ||
if (existingPresenceHandler) { | ||
promiseToHandler.set(handledP, existingPresenceHandler); | ||
promiseToPresence.set(handledP, presence); | ||
return continueForwarding(null, handledP); | ||
@@ -307,8 +329,14 @@ } | ||
HandledPromise.prototype = Promise.prototype; | ||
HandledPromise.prototype = promiseProto; | ||
Object.setPrototypeOf(HandledPromise, Promise); | ||
// Uncomment this line if needed for conformance to the proposal. | ||
// Currently the proposal does not specify this, but we might change | ||
// our mind. | ||
// Object.setPrototypeOf(HandledPromise, Promise); | ||
function isFrozenPromiseThen(p) { | ||
return ( | ||
isFrozen(p) && | ||
getPrototypeOf(p) === promiseProto && | ||
promiseResolve(p) === p && | ||
gopd(p, 'then') === undefined && | ||
gopd(promiseProto, 'then').value === originalThen // unnecessary under SES | ||
); | ||
} | ||
@@ -337,11 +365,54 @@ const staticMethods = harden({ | ||
// Resolving a Presence returns the pre-registered handled promise. | ||
const handledPromise = presenceToPromise.get(value); | ||
if (handledPromise) { | ||
return handledPromise; | ||
let resolvedPromise = presenceToPromise.get(value); | ||
if (!resolvedPromise) { | ||
resolvedPromise = promiseResolve(value); | ||
} | ||
const basePromise = Promise.resolve(value); | ||
if (basePromise === value) { | ||
// Prevent any proxy trickery. | ||
harden(resolvedPromise); | ||
if (isFrozenPromiseThen(resolvedPromise)) { | ||
return resolvedPromise; | ||
} | ||
// Assimilate the thenable. | ||
const executeThen = (resolve, reject) => | ||
resolvedPromise.then(resolve, reject); | ||
return harden( | ||
promiseResolve().then(_ => new HandledPromise(executeThen)), | ||
); | ||
}, | ||
// TODO verify that this is safe to provide universally, i.e., | ||
// that by itself it doesn't provide access to mutable state in | ||
// ways that violate normal ocap module purity rules. The claim | ||
// that it does not rests on the handled promise itself being | ||
// necessary to perceive this mutable state. In that sense, we | ||
// can think of the right to perceive it, and of access to the | ||
// target, as being in the handled promise. Note that a .then on | ||
// the handled promise will already provide async access to the | ||
// target, so the only additional authorities are: 1) | ||
// synchronous access for handled promises only, and thus 2) the | ||
// ability to tell, from the client side, whether a promise is | ||
// handled. Or, at least, the ability to tell given that the | ||
// promise is already fulfilled. | ||
unwrap(value) { | ||
// This check for Thenable is safe, since in a remote-object | ||
// environment, our comms system will defend against remote | ||
// objects being represented as a tricky local Proxy, otherwise | ||
// it is guaranteed to be local and therefore synchronous enough. | ||
if (Object(value) !== value || !('then' in value)) { | ||
// Not a Thenable, so return it. | ||
// This means that local objects will pass through without error. | ||
return value; | ||
} | ||
return handledPromiseResolve(value); | ||
// Try to look up the HandledPromise. | ||
ensureMaps(); | ||
const pr = presenceToPromise.get(value) || value; | ||
// Find the fulfilled presence for that HandledPromise. | ||
const presence = promiseToPresence.get(pr); | ||
if (!presence) { | ||
throw TypeError( | ||
`Value is a Thenble but not a HandledPromise fulfilled to a presence`, | ||
); | ||
} | ||
return presence; | ||
}, | ||
@@ -425,3 +496,3 @@ }); | ||
handledPromiseResolve = Promise.resolve.bind(HandledPromise); | ||
promiseResolve = Promise.resolve.bind(Promise); | ||
return harden(HandledPromise); | ||
@@ -428,0 +499,0 @@ } |
@@ -63,3 +63,3 @@ import harden from '@agoric/harden'; | ||
get(_target, prop) { | ||
return HandledPromise.get(x, prop); | ||
return harden(HandledPromise.get(x, prop)); | ||
}, | ||
@@ -70,2 +70,3 @@ }); | ||
E.resolve = HandledPromise.resolve; | ||
E.unwrap = HandledPromise.unwrap; | ||
@@ -77,4 +78,13 @@ return harden(E); | ||
const { defineProperties, getOwnPropertyDescriptors } = Object; | ||
const { | ||
defineProperties, | ||
getOwnPropertyDescriptors, | ||
getOwnPropertyDescriptor: gopd, | ||
getPrototypeOf, | ||
isFrozen, | ||
} = Object; | ||
const { prototype: promiseProto } = Promise; | ||
const { then: originalThen } = promiseProto; | ||
// 'E' and 'HandledPromise' are exports of the module | ||
@@ -115,2 +125,3 @@ | ||
let promiseToHandler; | ||
let promiseToPresence; // only for HandledPromise.unwrap | ||
function ensureMaps() { | ||
@@ -121,2 +132,3 @@ if (!presenceToHandler) { | ||
promiseToHandler = new WeakMap(); | ||
promiseToPresence = new WeakMap(); | ||
} | ||
@@ -129,3 +141,3 @@ } | ||
let handle; | ||
let handledPromiseResolve; | ||
let promiseResolve; | ||
@@ -174,2 +186,5 @@ function HandledPromise(executor, unfulfilledHandler = undefined) { | ||
}); | ||
// A failed interlock should not be recorded as an unhandled rejection. | ||
// It will bubble up to the HandledPromise itself. | ||
interlockP.catch(_ => {}); | ||
@@ -233,2 +248,3 @@ const makePostponed = postponedOperation => { | ||
presenceToPromise.set(resolvedPresence, handledP); | ||
promiseToPresence.set(handledP, resolvedPresence); | ||
presenceToHandler.set(resolvedPresence, presenceHandler); | ||
@@ -273,6 +289,12 @@ | ||
// See if the target is a presence we already know of. | ||
const presence = await target; | ||
let presence; | ||
try { | ||
presence = HandledPromise.unwrap(target); | ||
} catch (e) { | ||
presence = await target; | ||
} | ||
const existingPresenceHandler = presenceToHandler.get(presence); | ||
if (existingPresenceHandler) { | ||
promiseToHandler.set(handledP, existingPresenceHandler); | ||
promiseToPresence.set(handledP, presence); | ||
return continueForwarding(null, handledP); | ||
@@ -301,8 +323,14 @@ } | ||
HandledPromise.prototype = Promise.prototype; | ||
HandledPromise.prototype = promiseProto; | ||
Object.setPrototypeOf(HandledPromise, Promise); | ||
// Uncomment this line if needed for conformance to the proposal. | ||
// Currently the proposal does not specify this, but we might change | ||
// our mind. | ||
// Object.setPrototypeOf(HandledPromise, Promise); | ||
function isFrozenPromiseThen(p) { | ||
return ( | ||
isFrozen(p) && | ||
getPrototypeOf(p) === promiseProto && | ||
promiseResolve(p) === p && | ||
gopd(p, 'then') === undefined && | ||
gopd(promiseProto, 'then').value === originalThen // unnecessary under SES | ||
); | ||
} | ||
@@ -331,11 +359,54 @@ const staticMethods = harden({ | ||
// Resolving a Presence returns the pre-registered handled promise. | ||
const handledPromise = presenceToPromise.get(value); | ||
if (handledPromise) { | ||
return handledPromise; | ||
let resolvedPromise = presenceToPromise.get(value); | ||
if (!resolvedPromise) { | ||
resolvedPromise = promiseResolve(value); | ||
} | ||
const basePromise = Promise.resolve(value); | ||
if (basePromise === value) { | ||
// Prevent any proxy trickery. | ||
harden(resolvedPromise); | ||
if (isFrozenPromiseThen(resolvedPromise)) { | ||
return resolvedPromise; | ||
} | ||
// Assimilate the thenable. | ||
const executeThen = (resolve, reject) => | ||
resolvedPromise.then(resolve, reject); | ||
return harden( | ||
promiseResolve().then(_ => new HandledPromise(executeThen)), | ||
); | ||
}, | ||
// TODO verify that this is safe to provide universally, i.e., | ||
// that by itself it doesn't provide access to mutable state in | ||
// ways that violate normal ocap module purity rules. The claim | ||
// that it does not rests on the handled promise itself being | ||
// necessary to perceive this mutable state. In that sense, we | ||
// can think of the right to perceive it, and of access to the | ||
// target, as being in the handled promise. Note that a .then on | ||
// the handled promise will already provide async access to the | ||
// target, so the only additional authorities are: 1) | ||
// synchronous access for handled promises only, and thus 2) the | ||
// ability to tell, from the client side, whether a promise is | ||
// handled. Or, at least, the ability to tell given that the | ||
// promise is already fulfilled. | ||
unwrap(value) { | ||
// This check for Thenable is safe, since in a remote-object | ||
// environment, our comms system will defend against remote | ||
// objects being represented as a tricky local Proxy, otherwise | ||
// it is guaranteed to be local and therefore synchronous enough. | ||
if (Object(value) !== value || !('then' in value)) { | ||
// Not a Thenable, so return it. | ||
// This means that local objects will pass through without error. | ||
return value; | ||
} | ||
return handledPromiseResolve(value); | ||
// Try to look up the HandledPromise. | ||
ensureMaps(); | ||
const pr = presenceToPromise.get(value) || value; | ||
// Find the fulfilled presence for that HandledPromise. | ||
const presence = promiseToPresence.get(pr); | ||
if (!presence) { | ||
throw TypeError( | ||
`Value is a Thenble but not a HandledPromise fulfilled to a presence`, | ||
); | ||
} | ||
return presence; | ||
}, | ||
@@ -419,3 +490,3 @@ }); | ||
handledPromiseResolve = Promise.resolve.bind(HandledPromise); | ||
promiseResolve = Promise.resolve.bind(Promise); | ||
return harden(HandledPromise); | ||
@@ -422,0 +493,0 @@ } |
@@ -69,3 +69,3 @@ (function (global, factory) { | ||
get(_target, prop) { | ||
return HandledPromise.get(x, prop); | ||
return harden(HandledPromise.get(x, prop)); | ||
}, | ||
@@ -76,2 +76,3 @@ }); | ||
E.resolve = HandledPromise.resolve; | ||
E.unwrap = HandledPromise.unwrap; | ||
@@ -83,4 +84,13 @@ return harden(E); | ||
const { defineProperties, getOwnPropertyDescriptors } = Object; | ||
const { | ||
defineProperties, | ||
getOwnPropertyDescriptors, | ||
getOwnPropertyDescriptor: gopd, | ||
getPrototypeOf, | ||
isFrozen, | ||
} = Object; | ||
const { prototype: promiseProto } = Promise; | ||
const { then: originalThen } = promiseProto; | ||
// 'E' and 'HandledPromise' are exports of the module | ||
@@ -121,2 +131,3 @@ | ||
let promiseToHandler; | ||
let promiseToPresence; // only for HandledPromise.unwrap | ||
function ensureMaps() { | ||
@@ -127,2 +138,3 @@ if (!presenceToHandler) { | ||
promiseToHandler = new WeakMap(); | ||
promiseToPresence = new WeakMap(); | ||
} | ||
@@ -135,3 +147,3 @@ } | ||
let handle; | ||
let handledPromiseResolve; | ||
let promiseResolve; | ||
@@ -180,2 +192,5 @@ function HandledPromise(executor, unfulfilledHandler = undefined) { | ||
}); | ||
// A failed interlock should not be recorded as an unhandled rejection. | ||
// It will bubble up to the HandledPromise itself. | ||
interlockP.catch(_ => {}); | ||
@@ -239,2 +254,3 @@ const makePostponed = postponedOperation => { | ||
presenceToPromise.set(resolvedPresence, handledP); | ||
promiseToPresence.set(handledP, resolvedPresence); | ||
presenceToHandler.set(resolvedPresence, presenceHandler); | ||
@@ -279,6 +295,12 @@ | ||
// See if the target is a presence we already know of. | ||
const presence = await target; | ||
let presence; | ||
try { | ||
presence = HandledPromise.unwrap(target); | ||
} catch (e) { | ||
presence = await target; | ||
} | ||
const existingPresenceHandler = presenceToHandler.get(presence); | ||
if (existingPresenceHandler) { | ||
promiseToHandler.set(handledP, existingPresenceHandler); | ||
promiseToPresence.set(handledP, presence); | ||
return continueForwarding(null, handledP); | ||
@@ -307,8 +329,14 @@ } | ||
HandledPromise.prototype = Promise.prototype; | ||
HandledPromise.prototype = promiseProto; | ||
Object.setPrototypeOf(HandledPromise, Promise); | ||
// Uncomment this line if needed for conformance to the proposal. | ||
// Currently the proposal does not specify this, but we might change | ||
// our mind. | ||
// Object.setPrototypeOf(HandledPromise, Promise); | ||
function isFrozenPromiseThen(p) { | ||
return ( | ||
isFrozen(p) && | ||
getPrototypeOf(p) === promiseProto && | ||
promiseResolve(p) === p && | ||
gopd(p, 'then') === undefined && | ||
gopd(promiseProto, 'then').value === originalThen // unnecessary under SES | ||
); | ||
} | ||
@@ -337,11 +365,54 @@ const staticMethods = harden({ | ||
// Resolving a Presence returns the pre-registered handled promise. | ||
const handledPromise = presenceToPromise.get(value); | ||
if (handledPromise) { | ||
return handledPromise; | ||
let resolvedPromise = presenceToPromise.get(value); | ||
if (!resolvedPromise) { | ||
resolvedPromise = promiseResolve(value); | ||
} | ||
const basePromise = Promise.resolve(value); | ||
if (basePromise === value) { | ||
// Prevent any proxy trickery. | ||
harden(resolvedPromise); | ||
if (isFrozenPromiseThen(resolvedPromise)) { | ||
return resolvedPromise; | ||
} | ||
// Assimilate the thenable. | ||
const executeThen = (resolve, reject) => | ||
resolvedPromise.then(resolve, reject); | ||
return harden( | ||
promiseResolve().then(_ => new HandledPromise(executeThen)), | ||
); | ||
}, | ||
// TODO verify that this is safe to provide universally, i.e., | ||
// that by itself it doesn't provide access to mutable state in | ||
// ways that violate normal ocap module purity rules. The claim | ||
// that it does not rests on the handled promise itself being | ||
// necessary to perceive this mutable state. In that sense, we | ||
// can think of the right to perceive it, and of access to the | ||
// target, as being in the handled promise. Note that a .then on | ||
// the handled promise will already provide async access to the | ||
// target, so the only additional authorities are: 1) | ||
// synchronous access for handled promises only, and thus 2) the | ||
// ability to tell, from the client side, whether a promise is | ||
// handled. Or, at least, the ability to tell given that the | ||
// promise is already fulfilled. | ||
unwrap(value) { | ||
// This check for Thenable is safe, since in a remote-object | ||
// environment, our comms system will defend against remote | ||
// objects being represented as a tricky local Proxy, otherwise | ||
// it is guaranteed to be local and therefore synchronous enough. | ||
if (Object(value) !== value || !('then' in value)) { | ||
// Not a Thenable, so return it. | ||
// This means that local objects will pass through without error. | ||
return value; | ||
} | ||
return handledPromiseResolve(value); | ||
// Try to look up the HandledPromise. | ||
ensureMaps(); | ||
const pr = presenceToPromise.get(value) || value; | ||
// Find the fulfilled presence for that HandledPromise. | ||
const presence = promiseToPresence.get(pr); | ||
if (!presence) { | ||
throw TypeError( | ||
`Value is a Thenble but not a HandledPromise fulfilled to a presence`, | ||
); | ||
} | ||
return presence; | ||
}, | ||
@@ -425,3 +496,3 @@ }); | ||
handledPromiseResolve = Promise.resolve.bind(HandledPromise); | ||
promiseResolve = Promise.resolve.bind(Promise); | ||
return harden(HandledPromise); | ||
@@ -428,0 +499,0 @@ } |
{ | ||
"name": "@agoric/eventual-send", | ||
"version": "0.5.0", | ||
"version": "0.5.1", | ||
"description": "Extend a Promise class to implement the eventual-send API", | ||
"main": "dist/eventual-send.cjs.js", | ||
"module": "dist/eventual-send.esm.js", | ||
"module": "src/index.js", | ||
"browser": "dist/eventual-send.umd.js", | ||
@@ -12,4 +12,4 @@ "types": "src/index.d.ts", | ||
"build": "rollup -c", | ||
"lint-fix": "eslint --fix '**/*.{js,jsx}'", | ||
"lint-check": "eslint '**/*.{js,jsx}'" | ||
"lint-fix": "eslint --fix '**/*.js'", | ||
"lint-check": "eslint '**/*.js'" | ||
}, | ||
@@ -43,5 +43,5 @@ "repository": { | ||
"files": [ | ||
"src/index.d.ts", | ||
"src", | ||
"dist" | ||
] | ||
} |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
79049
11
1821
0