diff --git a/packages/eventual-send/src/E.js b/packages/eventual-send/src/E.js index 889eccd0fd7..a984b2dfff8 100644 --- a/packages/eventual-send/src/E.js +++ b/packages/eventual-send/src/E.js @@ -1,13 +1,8 @@ -/* global globalThis window */ +/* global SES */ // eslint-disable-next-line spaced-comment /// -// Shim globalThis when we don't have it. -if (typeof globalThis === 'undefined') { - const myGlobal = typeof window === 'undefined' ? global : window; - myGlobal.globalThis = myGlobal; -} -const harden = (globalThis.SES && globalThis.SES.harden) || Object.freeze; +const harden = (typeof SES !== 'undefined' && SES.harden) || Object.freeze; const readOnlyProxy = { set(_target, _prop, _value) { @@ -57,103 +52,21 @@ function EProxyHandler(x, HandledPromise) { export default function makeE(HandledPromise) { function E(x) { const handler = EProxyHandler(x, HandledPromise); - return harden(new Proxy({}, handler)); + return harden(new Proxy(() => {}, handler)); } - const makeEGetterProxy = (x, wrap = o => o) => - new Proxy(Object.create(null), { - ...readOnlyProxy, - has(_target, _prop) { - return true; - }, - get(_target, prop) { - return wrap(HandledPromise.get(x, prop)); - }, - }); - - const makeEDeleterProxy = (x, wrap = o => o) => + const makeEGetterProxy = x => new Proxy(Object.create(null), { ...readOnlyProxy, has(_target, _prop) { return true; }, get(_target, prop) { - return wrap(HandledPromise.delete(x, prop)); - }, - }); - - const makeEHasProxy = (x, wrap = o => o) => - new Proxy(Object.create(null), { - ...readOnlyProxy, - has(_target, _prop) { - return true; - }, - get(_target, prop) { - return wrap(HandledPromise.has(x, prop)); - }, - }); - - const makeESetterProxy = (x, wrap = o => o) => - new Proxy(Object.create(null), { - ...readOnlyProxy, - has(_target, _prop) { - return true; - }, - get(_target, prop) { - return harden(value => wrap(HandledPromise.set(x, prop, value))); - }, - }); - - const makeEMethodProxy = (x, wrap = o => o) => - new Proxy((..._args) => {}, { - ...readOnlyProxy, - has(_target, _prop) { - return true; - }, - get(_target, prop) { - return harden((...args) => - wrap(HandledPromise.applyMethod(x, prop, args)), - ); - }, - apply(_target, _thisArg, args = []) { - return wrap(HandledPromise.applyFunction(x, args)); + return HandledPromise.get(x, prop); }, }); E.G = o => makeEGetterProxy(o); - E.H = o => makeEHasProxy(o); - E.D = o => makeEDeleterProxy(o); - E.S = o => makeESetterProxy(o); - E.M = o => makeEMethodProxy(o); - - const EChain = x => - harden({ - get G() { - // Return getter. - return makeEGetterProxy(x, EChain); - }, - get D() { - // Return deleter. - return makeEDeleterProxy(x, EChain); - }, - get H() { - // Return has. - return makeEHasProxy(x, EChain); - }, - get S() { - // Return setter. - return makeESetterProxy(x, EChain); - }, - get M() { - // Return method-caller. - return makeEMethodProxy(x, EChain); - }, - get P() { - // Return as promise. - return Promise.resolve(x); - }, - }); - E.C = EChain; return harden(E); } diff --git a/packages/eventual-send/src/index.d.ts b/packages/eventual-send/src/index.d.ts index d5fce17a393..74f93839dd4 100644 --- a/packages/eventual-send/src/index.d.ts +++ b/packages/eventual-send/src/index.d.ts @@ -21,47 +21,13 @@ interface HandledPromiseConstructor { declare const HandledPromise: HandledPromiseConstructor; -interface ESingleMethod { - [prop: Property]: (...args) => U; +interface ESingleMethod { + (...args: unknown[]): Promise; + [prop: Property]: (...args) => Promise; } -interface EChain { - M: EChainMethod>; - G: EChainGet>; - S: EChainSet>; - D: EChainDelete>; - P: Promise; - sendOnly: EChainSendOnly; -} - -interface EChainSendOnly { - M: EChainMethod; - G: EChainGet; - S: EChainSet; - D: EChainDelete; -} - -interface EChainMethod { - (...args: unknown[]): U; - [prop: Property]: (...args: unknown) => U; -} - -interface EChainGet { - [prop: Property]: U; -} - -interface EChainSet { - /** - * Eventually set the prop property. - */ - [prop: Property]: (value: unknown) => U; -} - -interface EChainDelete { - /** - * Eventually delete the prop property. - */ - [prop: Property]: U is void ? U : EChain; +interface ESingleGet { + [prop: Property]: Promise; } interface EProxy { @@ -74,16 +40,18 @@ interface EProxy { * @param {*} x target for method call * @returns {ESingleMethod} method call proxy */ - (x: unknown): ESingleMethod>; - sendOnly: (x: unknown) => ESingleMethod; + (x: unknown): ESingleMethod; + sendOnly: (x: unknown) => ESingleMethod; /** - * E.C(x) returns a chain where operations are selected by - * uppercase single-letter selectors. + * E.G(x) returns a proxy on which you can get arbitrary properties. + * Each of these properties returns a promise. The property will be + * taken from whatever 'x' designates (or resolves to) in a future turn, + * not this one. * - * @param {*} x target for first operation - * @returns {EChain} + * @param {*} x target for property get + * @returns {ESingleGet} property get proxy */ - C(x: unknown): EChain; + G(x: unknown): ESingleGet; } export const E: EProxy; diff --git a/packages/eventual-send/src/index.js b/packages/eventual-send/src/index.js index 824e6a218b2..14da0151908 100644 --- a/packages/eventual-send/src/index.js +++ b/packages/eventual-send/src/index.js @@ -1,4 +1,4 @@ -/* global globalThis */ +/* global HandledPromise SES globalThis window */ import makeE from './E'; @@ -8,18 +8,27 @@ import makeE from './E'; // import { HandledPromise, E } from '@agoric/eventual-send'; // ... -// TODO: Maybe rename the global HandledPromise to something only the tildot rewriter uses. -if (!globalThis.HandledPromise) { +// eslint-disable-next-line import/no-mutable-exports +let hp; +if (typeof HandledPromise === 'undefined') { /* eslint-disable no-use-before-define */ + if (typeof globalThis === 'undefined') { + const myGlobal = typeof window === 'undefined' ? global : window; + myGlobal.globalThis = window; + } + // Install the shim as best we can. maybeExtendPromise(Promise); globalThis.HandledPromise = makeHandledPromise(Promise); + hp = globalThis.HandledPromise; /* eslint-enable no-use-before-define */ +} else { + hp = HandledPromise; } // Provide a handled platform Promise if SES has not run. -export const { HandledPromise } = globalThis; -export const E = makeE(HandledPromise); +export { hp as HandledPromise }; +export const E = makeE(hp); // the following methods (makeHandledPromise and maybeExtendPromise) are part // of the shim, and will not be exported by the module once the feature @@ -28,10 +37,10 @@ export const E = makeE(HandledPromise); // Create HandledPromise static methods as a bridge from v0.2.4 // to new proposal support (wavy dot's infrastructure). export function makeHandledPromise(EPromise) { - const harden = (globalThis.SES && globalThis.SES.harden) || Object.freeze; + const harden = (typeof SES !== 'undefined' && SES.harden) || Object.freeze; - // TODO: Use HandledPromise.resolve to store our weakmap, and - // install it on Promise.resolve. + // TODO: Use HandledPromise.resolve to store our weakmap, instead of + // monkey-patching Promise.resolve. const staticMethods = { get(target, key) { return EPromise.resolve(target).get(key); @@ -39,25 +48,6 @@ export function makeHandledPromise(EPromise) { getSendOnly(target, key) { EPromise.resolve(target).get(key); }, - has(target, key) { - return EPromise.resolve(target).has(key); - }, - hasSendOnly(target, key) { - EPromise.resolve(target).has(key); - }, - set(target, key, val) { - return EPromise.resolve(target).put(key, val); - }, - setSendOnly(target, key, val) { - EPromise.resolve(target).put(key, val); - }, - // TODO: Change HandledPromise.delete to HandledPromise.deleteProperty - delete(target, key) { - return EPromise.resolve(target).delete(key); - }, - deleteSendOnly(target, key) { - EPromise.resolve(target).delete(key); - }, applyFunction(target, args) { return EPromise.resolve(target).post(undefined, args); }, @@ -161,18 +151,6 @@ export function maybeExtendPromise(Promise) { return handle(this, 'GET', key); }, - has(key) { - return handle(this, 'HAS', key); - }, - - put(key, val) { - return handle(this, 'PUT', key, val); - }, - - delete(key) { - return handle(this, 'DELETE', key); - }, - post(optKey, args) { return handle(this, 'POST', optKey, args); }, @@ -261,9 +239,6 @@ export function maybeExtendPromise(Promise) { unfulfilledHandler = { GET: makePostponed('get'), - HAS: makePostponed('has'), - PUT: makePostponed('put'), - DELETE: makePostponed('delete'), POST: makePostponed('post'), }; } @@ -391,9 +366,6 @@ export function maybeExtendPromise(Promise) { forwardingHandler = { GET: makeForwarder('GET', (o, key) => o[key]), - HAS: makeForwarder('HAS', (o, key) => key in o), - PUT: makeForwarder('PUT', (o, key, val) => (o[key] = val)), - DELETE: makeForwarder('DELETE', (o, key) => delete o[key]), POST: makeForwarder('POST', (o, optKey, args) => { if (optKey === undefined || optKey === null) { return o(...args); diff --git a/packages/eventual-send/test/test-e.js b/packages/eventual-send/test/test-e.js index 116f2c369d9..96ef0db3e3c 100644 --- a/packages/eventual-send/test/test-e.js +++ b/packages/eventual-send/test/test-e.js @@ -18,7 +18,7 @@ test('E method calls', async t => { } }); -test('E.* shortcuts', async t => { +test('E shortcuts', async t => { try { const x = { name: 'buddy', @@ -32,56 +32,13 @@ test('E.* shortcuts', async t => { return `${greeting}, ${this.name}!`; }, }; - t.equal(await E.M(x).hello('Hello'), 'Hello, buddy!', 'method call works'); + t.equal(await E(x).hello('Hello'), 'Hello, buddy!', 'method call works'); t.equal( - await E.M(await E.G(await E.G(x).y).fn)(4), + await E(await E.G(await E.G(x).y).fn)(4), 8, 'anonymous method works', ); t.equal(await E.G(x).val, 123, 'property get'); - t.equal(await E.S(x).val(999), 999, 'property set'); - t.equal(x.val, 999, 'property set works'); - t.equal(await E.D(x).val, true, 'property delete'); - t.equal(x.val, undefined, 'delete worked'); - await t.rejects( - E.D(await E.G(x).y).val2, - TypeError, - 'property delete fails', - ); - t.equal(x.y.val2, 456, 'delete failed'); - } catch (e) { - t.isNot(e, e, 'unexpected exception'); - } finally { - t.end(); - } -}); - -test('E.C chains', async t => { - try { - const x = { - name: 'buddy', - val: 123, - y: Object.freeze({ - val2: 456, - name2: 'holly', - fn: n => 2 * n, - }), - hello(greeting) { - return `${greeting}, ${this.name}!`; - }, - }; - const xC = E.C(x); - t.equal(await xC.M.hello('Hello').P, 'Hello, buddy!', 'method call works'); - t.equal(await xC.G.y.G.fn.M(4).P, 8, 'anonymous method works'); - t.equal(await xC.G.val.P, 123, 'property get'); - t.equal(await xC.S.val(999).P, 999, 'property set'); - t.equal(await xC.H.val.P, true, 'property has'); - t.equal(await xC.H.notval.P, false, 'missing property has not'); - t.equal(x.val, 999, 'property set works'); - t.equal(await xC.D.val.P, true, 'property delete'); - t.equal(x.val, undefined, 'delete worked'); - await t.rejects(xC.G.y.D.val2.P, TypeError, 'property delete fails'); - t.equal(x.y.val2, 456, 'delete failed'); } catch (e) { t.isNot(e, e, 'unexpected exception'); } finally { diff --git a/packages/eventual-send/test/test.js b/packages/eventual-send/test/test.js index 946370092ce..bdce1fc33d4 100644 --- a/packages/eventual-send/test/test.js +++ b/packages/eventual-send/test/test.js @@ -153,12 +153,6 @@ test('Promise.makeHandled expected errors', async t => { GET(key) { return key; }, - DELETE(_key) { - return true; - }, - PUT(key, value) { - return value; - }, POST(key, args) { return args; }, @@ -188,16 +182,6 @@ test('Promise.makeHandled expected errors', async t => { `missing ${method} defaults`, ); break; - case 'PUT': - t.equals( - await Promise.makeHandled((_, _2, rwp) => rwp(handler2)).put( - 'foo', - 123, - ), - 123, - `missing ${method} defaults`, - ); - break; case 'POST': t.equals( await Promise.makeHandled((_, _2, rwp) => { @@ -212,15 +196,6 @@ test('Promise.makeHandled expected errors', async t => { `missing ${method} defaults`, ); break; - case 'DELETE': - t.equals( - await Promise.makeHandled((_, _2, rwp) => rwp(handler2)).delete( - 'foo', - ), - true, - `missing ${method} defaults`, - ); - break; default: throw TypeError(`Unrecognized method type ${method}`); } @@ -257,12 +232,6 @@ test('Promise.makeHandled(executor, undefined)', async t => { GET(p, key) { return o[key]; }, - PUT(p, key, value) { - return (o[key] = value); - }, - DELETE(p, key) { - return delete o[key]; - }, POST(p, key, args) { return o[key](...args); }, @@ -278,16 +247,6 @@ test('Promise.makeHandled(executor, undefined)', async t => { ); t.equal(await handledP.get('str'), 'my string', `.get works`); t.equal(await handledP.get('num'), 123, `.get num works`); - t.equal(await handledP.put('num', 789), 789, `.put works`); - t.equal(await handledP.get('num'), 789, `.put changes assignment`); - t.equal(await handledP.delete('str'), true, `.delete works`); - t.equal(await handledP.get('str'), undefined, `.delete actually deletes`); - t.equal(await handledP.delete('str'), true, `.delete works second time`); - t.equal( - await handledP.get('str'), - undefined, - `.delete second time still deletes`, - ); t.equal( await handledP.invoke('hello', 'World'), 'Hello, World', @@ -427,19 +386,6 @@ test('get', async t => { } }); -test('put', async t => { - try { - const a = [123, 456, 789]; - const ep = Promise.resolve(a); - t.equal(await ep.put(1, 999), 999, `.put works`); - t.deepEqual(a, [123, 999, 789], `.put actually changed`); - } catch (e) { - t.assert(false, `Unexpected exception ${e}`); - } finally { - t.end(); - } -}); - test('post', async t => { try { const fn = () => 'hello';